Beispiel #1
0
def function(outputFolder, yearAFolder, yearBFolder, lsOption, slopeAngle,
             rData, soilData, soilCode, YearALCData, YearALCCode, YearBLCData,
             YearBLCCode, YearAPData, YearBPData, saveFactors):

    try:
        # Set temporary variables
        prefix = os.path.join(arcpy.env.scratchGDB, "rusleAcc_")

        clipA = prefix + "clipA"
        clipB = prefix + "clipB"
        lossA = prefix + "lossA"
        lossB = prefix + "lossB"
        diffLoss = prefix + "diffLoss"
        diffNullZero = prefix + "diffNullZero"

        # Set output filenames
        soilLossA = os.path.join(outputFolder, "soillossA")
        soilLossB = os.path.join(outputFolder, "soillossB")
        soilLossDiff = os.path.join(outputFolder, "soillossDiff")

        # Set soil option for both years
        soilOption = 'LocalSoil'

        # Set LC option for both years
        lcOption = 'LocalCfactor'

        ################################
        ### Running RUSLE for Year A ###
        ################################

        log.info('*****************************')
        log.info('Running RUSLE tool for Year A')
        log.info('*****************************')

        filesA = common.getFilenames('preprocess', yearAFolder)
        studyMaskA = filesA.studyareamask

        # Call RUSLE function for Year A
        soilLoss = RUSLE.function(outputFolder, yearAFolder, lsOption,
                                  slopeAngle, soilOption, soilData, soilCode,
                                  lcOption, YearALCData, YearALCCode, rData,
                                  saveFactors, YearAPData)

        arcpy.CopyRaster_management(soilLoss, soilLossA)

        # Delete intermediate files
        arcpy.Delete_management(soilLoss)

        ################################
        ### Running RUSLE for Year B ###
        ################################

        log.info('*****************************')
        log.info('Running RUSLE tool for Year B')
        log.info('*****************************')

        filesB = common.getFilenames('preprocess', yearBFolder)
        studyMaskB = filesB.studyareamask

        # Call RUSLE function for Year B
        soilLoss = RUSLE.function(outputFolder, yearBFolder, lsOption,
                                  slopeAngle, soilOption, soilData, soilCode,
                                  lcOption, YearBLCData, YearBLCCode, rData,
                                  saveFactors, YearBPData)

        arcpy.CopyRaster_management(soilLoss, soilLossB)

        # Delete intermediate files
        arcpy.Delete_management(soilLoss)

        #######################################################
        ### Calculate differences between Year A and Year B ###
        #######################################################

        log.info('*************************************************')
        log.info('Calculating differences between Year A and Year B')
        log.info('*************************************************')

        # Copy soil loss layers to temporary files
        arcpy.CopyRaster_management(soilLossA, lossA)
        arcpy.CopyRaster_management(soilLossB, lossB)

        diffTemp = Raster(lossB) - Raster(lossA)
        diffTemp.save(diffLoss)
        del diffTemp

        log.info('Removing the areas of zero difference')
        diffNullTemp = SetNull(diffLoss, diffLoss, "VALUE = 0")
        diffNullTemp.save(diffNullZero)
        del diffNullTemp

        arcpy.CopyRaster_management(diffNullZero, soilLossDiff)

        log.info("RUSLE accounts function completed successfully")

    except Exception:
        arcpy.AddError("RUSLE accounts function failed")
        raise

    finally:
        # Remove feature layers from memory
        try:
            for lyr in common.listFeatureLayers(locals()):
                arcpy.Delete_management(locals()[lyr])
                exec(lyr + ' = None') in locals()
        except Exception:
            pass
def function(params):

    try:
        ###################
        ### Read inputs ###
        ###################

        pText = common.paramsAsText(params)

        outputFolder = pText[1]
        inputDEM = common.fullPath(pText[2])
        inputStudyAreaMask = pText[3]
        inputLC = pText[4]
        lcCode = pText[5]
        inputSoil = pText[6]
        soilCode = pText[7]
        reconDEM = common.strToBool(pText[8])
        inputStreamNetwork = pText[9]
        streamAccThresh = pText[10]
        riverAccThresh = pText[11]
        smoothDropBuffer = pText[12]
        smoothDrop = pText[13]
        streamDrop = pText[14]
        rerun = common.strToBool(pText[15])

        log.info('Inputs read in')

        ###########################
        ### Tool initialisation ###
        ###########################

        # Create Baseline folder
        if not os.path.exists(outputFolder):
            os.mkdir(outputFolder)

        # Set up logging output to file
        log.setupLogging(outputFolder)

        # Run system checks
        common.runSystemChecks(outputFolder, rerun)

        # Set up progress log file
        progress.initProgress(outputFolder, rerun)

        # Write input params to XML
        common.writeParamsToXML(params, outputFolder, 'PreprocessDEM')

        log.info('Tool initialised')

        ########################
        ### Define filenames ###
        ########################

        files = common.getFilenames('preprocess', outputFolder)
        studyAreaMask = files.studyareamask
        outputLCras = files.lc_ras
        outputLCvec = files.lc_vec
        outputSoilras = files.soil_ras
        outputSoilvec = files.soil_vec

        ###############################
        ### Set temporary variables ###
        ###############################

        prefix = os.path.join(arcpy.env.scratchGDB, 'base_')

        DEMTemp = prefix + 'DEMTemp'
        clippedDEM = prefix + 'clippedDEM'
        clippedLC = prefix + 'clippedLC'
        clippedSoil = prefix + 'clippedSoil'
        clippedStreamNetwork = prefix + 'clippedStreamNetwork'

        studyAreaMaskTemp = prefix + "studyAreaMaskTemp"
        studyAreaMaskBuff = prefix + "studyAreaMaskBuff"
        studyAreaMaskDiss = prefix + "studyAreaMaskDiss"

        log.info('Temporary variables set')

        # Check formats of inputs
        lcFormat = arcpy.Describe(inputLC).dataType
        soilFormat = arcpy.Describe(inputSoil).dataType

        ###################
        ### Data checks ###
        ###################

        codeBlock = 'Data checks 1'
        if not progress.codeSuccessfullyRun(codeBlock, outputFolder, rerun):

            inputFiles = [inputDEM, inputStudyAreaMask, inputLC, inputSoil]
            if inputStreamNetwork is not None:
                inputFiles.append(inputStreamNetwork)

            for file in inputFiles:
                common.checkSpatialRef(file)

            # Set environment variables
            arcpy.env.snapRaster = inputDEM
            arcpy.env.cellSize = inputDEM
            arcpy.env.compression = "None"

            cellsizedem = float(
                arcpy.GetRasterProperties_management(inputDEM,
                                                     "CELLSIZEX").getOutput(0))

            # Get spatial references of DEM and study area mask
            DEMSpatRef = arcpy.Describe(inputDEM).SpatialReference
            maskSpatRef = arcpy.Describe(inputStudyAreaMask).SpatialReference

            # Reproject study area mask if it does not have the same coordinate system as the DEM
            if not common.equalProjections(DEMSpatRef, maskSpatRef):

                warning = "Study area mask does not have the same coordinate system as the DEM"
                log.warning(warning)
                common.logWarnings(outputFolder, warning)

                warning = "Mask coordinate system is " + maskSpatRef.Name + " while DEM coordinate system is " + DEMSpatRef.Name
                log.warning(warning)
                common.logWarnings(outputFolder, warning)

                warning = "Reprojecting study area mask to DEM coordinate system"
                log.warning(warning)
                common.logWarnings(outputFolder, warning)

                arcpy.Project_management(inputStudyAreaMask, studyAreaMaskTemp,
                                         DEMSpatRef)
                arcpy.CopyFeatures_management(studyAreaMaskTemp, studyAreaMask)
            else:
                arcpy.CopyFeatures_management(inputStudyAreaMask,
                                              studyAreaMask)

            # If DEM is large, clip it to a large buffer around the study area mask (~5km)
            inputDEM = baseline.clipLargeDEM(inputDEM, studyAreaMask)

            rasterInputFiles = []
            fcInputFiles = []

            # Sort land cover and soil into appropriate arrays based on data type
            if lcFormat in ['RasterDataset', 'RasterLayer']:
                rasterInputFiles.append(inputLC)
                outputLC = os.path.join(outputFolder, 'landcover')

            elif lcFormat in ['ShapeFile', 'FeatureClass']:
                fcInputFiles.append(inputLC)
                outputLC = os.path.join(outputFolder, 'landcover.shp')

            if soilFormat in ['RasterDataset', 'RasterLayer']:
                rasterInputFiles.append(inputSoil)
                outputSoil = os.path.join(outputFolder, 'soil')

            elif soilFormat in ['ShapeFile', 'FeatureClass']:
                fcInputFiles.append(inputSoil)
                outputSoil = os.path.join(outputFolder, 'soil.shp')

            if reconDEM is True and inputStreamNetwork is None:
                log.error(
                    'Cannot recondition the DEM without an input stream network'
                )
                log.error('Please provide an input stream network')
                sys.exit()

            # If the user has provided a stream network, add it to the list of inputs to check
            if inputStreamNetwork is not None:
                fcInputFiles.append(inputStreamNetwork)

            # Check that the inputs contain data
            for ras in rasterInputFiles:
                if ras is not None:

                    # Check file size
                    fileSizeGB = baseline.checkRasterSizeGB(ras)

                    if fileSizeGB < 1.0:
                        baseline.checkInputRaster(ras, outputFolder)

                    else:
                        log.warning(
                            "Cannot check if raster is empty or all NoData because it is too large"
                        )
                        log.warning(
                            "Please ensure this raster is not empty or all NoData: "
                            + str(ras))

            for fc in fcInputFiles:
                if fc is not None:
                    baseline.checkInputFC(fc, outputFolder)

            # Check that the land cover and soil FCs have the linking codes specified by the user
            if lcFormat in ['ShapeFile', 'FeatureClass']:
                if len(arcpy.ListFields(inputLC, lcCode)) != 1:
                    log.error('Field ' + lcCode +
                              'does not exist in feature class ' + inputLC)
                    sys.exit()

            if soilFormat in ['ShapeFile', 'FeatureClass']:
                if len(arcpy.ListFields(inputSoil, soilCode)) != 1:
                    log.error('Field ' + soilCode +
                              'does not exist in feature class ' + inputSoil)
                    sys.exit()

            progress.logProgress(codeBlock, outputFolder)

        ###############################
        ### Tidy up study area mask ###
        ###############################

        codeBlock = 'Tidy up study area mask'
        if not progress.codeSuccessfullyRun(codeBlock, outputFolder, rerun):

            # Check how many polygons are in the mask shapefile
            numPolysInMask = int(
                arcpy.GetCount_management(studyAreaMask).getOutput(0))
            if numPolysInMask > 1:

                # Reduce multiple features where possible
                arcpy.Union_analysis(studyAreaMask, studyAreaMaskDiss,
                                     "ONLY_FID", "", "NO_GAPS")
                arcpy.Dissolve_management(studyAreaMaskDiss, studyAreaMask, "",
                                          "", "SINGLE_PART", "DISSOLVE_LINES")

            # Buffer study area mask
            baseline.bufferMask(inputDEM,
                                studyAreaMask,
                                outputStudyAreaMaskBuff=studyAreaMaskBuff)
            log.info('Study area mask buffered')

            progress.logProgress(codeBlock, outputFolder)

        #######################
        ### Clip input data ###
        #######################

        codeBlock = 'Clip inputs'
        if not progress.codeSuccessfullyRun(codeBlock, outputFolder, rerun):

            baseline.clipInputs(outputFolder,
                                studyAreaMaskBuff,
                                inputDEM,
                                inputLC,
                                inputSoil,
                                inputStreamNetwork,
                                outputDEM=clippedDEM,
                                outputLC=clippedLC,
                                outputSoil=clippedSoil,
                                outputStream=clippedStreamNetwork)

            progress.logProgress(codeBlock, outputFolder)

        ##############################################
        ### Coverage checks on soil and land cover ###
        ##############################################

        codeBlock = 'Do coverage checks on clipped land cover and soil'
        if not progress.codeSuccessfullyRun(codeBlock, outputFolder, rerun):

            # Do coverage checks on land cover and soil and copy to outputFolder
            if lcFormat in ['RasterDataset', 'RasterLayer']:

                lcMask = common.extractRasterMask(clippedLC)
                common.checkCoverage(lcMask, studyAreaMaskBuff, inputLC)

                arcpy.CopyRaster_management(clippedLC, outputLCras)

            elif lcFormat in ['ShapeFile', 'FeatureClass']:
                lcMask = common.dissolvePolygon(clippedLC)
                common.checkCoverage(lcMask, studyAreaMaskBuff, inputLC)

                arcpy.CopyFeatures_management(clippedLC, outputLCvec)

            if soilFormat in ['RasterDataset', 'RasterLayer']:

                soilMask = common.extractRasterMask(clippedSoil)
                common.checkCoverage(soilMask, studyAreaMaskBuff, inputLC)

                arcpy.CopyRaster_management(clippedSoil, outputSoilras)

            elif soilFormat in ['ShapeFile', 'FeatureClass']:

                soilMask = common.dissolvePolygon(clippedSoil)
                common.checkCoverage(soilMask, studyAreaMaskBuff, inputSoil)

                arcpy.CopyFeatures_management(clippedSoil, outputSoilvec)

            progress.logProgress(codeBlock, outputFolder)

        ######################################
        ### Convert LC and soil to rasters ###
        ######################################

        # For the RUSLE tool, the LC and soil must be in raster format

        codeBlock = 'Convert land cover and soil to rasters'
        if not progress.codeSuccessfullyRun(codeBlock, outputFolder, rerun):

            if lcFormat in ['ShapeFile', 'FeatureClass']:
                arcpy.PolygonToRaster_conversion(clippedLC, lcCode,
                                                 outputLCras, "CELL_CENTER",
                                                 "", cellsizedem)
                log.info('Land cover raster produced')

            if soilFormat in ['ShapeFile', 'FeatureClass']:
                arcpy.PolygonToRaster_conversion(clippedSoil, soilCode,
                                                 outputSoilras, "CELL_CENTER",
                                                 "", cellsizedem)
                log.info('Soil raster produced')

            # Delete intermediate files
            arcpy.Delete_management(clippedLC)
            arcpy.Delete_management(clippedSoil)

            progress.logProgress(codeBlock, outputFolder)

        ###########################
        ### Run HydTopo process ###
        ###########################

        log.info("*** Preprocessing DEM ***")
        preprocess_dem.function(outputFolder, clippedDEM, studyAreaMask,
                                clippedStreamNetwork, streamAccThresh,
                                riverAccThresh, smoothDropBuffer, smoothDrop,
                                streamDrop, reconDEM, rerun)

    except Exception:
        arcpy.SetParameter(0, False)
        log.exception("Preprocessing DEM functions did not complete")
        raise
Beispiel #3
0
def function(outputFolder,
             DEM,
             studyAreaMask,
             streamInput,
             minAccThresh,
             majAccThresh,
             smoothDropBuffer,
             smoothDrop,
             streamDrop,
             reconDEM,
             rerun=False):

    try:
        # Set environment variables
        arcpy.env.compression = "None"
        arcpy.env.snapRaster = DEM
        arcpy.env.extent = DEM
        arcpy.env.cellSize = arcpy.Describe(DEM).meanCellWidth

        ########################
        ### Define filenames ###
        ########################

        files = common.getFilenames('preprocess', outputFolder)

        rawDEM = files.rawDEM
        hydDEM = files.hydDEM
        hydFDR = files.hydFDR
        hydFDRDegrees = files.hydFDRDegrees
        hydFAC = files.hydFAC
        streamInvRas = files.streamInvRas  # Inverse stream raster - 0 for stream, 1 for no stream
        streams = files.streams
        streamDisplay = files.streamDisplay
        multRaster = files.multRaster
        hydFACInt = files.hydFACInt
        slopeRawDeg = files.slopeRawDeg
        slopeRawPer = files.slopeRawPer
        slopeHydDeg = files.slopeHydDeg
        slopeHydPer = files.slopeHydPer

        ###############################
        ### Set temporary variables ###
        ###############################

        prefix = os.path.join(arcpy.env.scratchGDB, "base_")

        cellSizeDEM = float(arcpy.env.cellSize)

        burnedDEM = prefix + "burnedDEM"
        streamAccHaFile = prefix + "streamAccHa"
        rawFDR = prefix + "rawFDR"
        allPolygonSinks = prefix + "allPolygonSinks"
        DEMTemp = prefix + "DEMTemp"
        hydFACTemp = prefix + "hydFACTemp"

        # Saved as .tif as did not save as ESRI grid on server
        streamsRasterFile = os.path.join(arcpy.env.scratchFolder,
                                         "base_") + "StreamsRaster.tif"

        ###############################
        ### Save DEM to base folder ###
        ###############################

        codeBlock = 'Save DEM'
        if not progress.codeSuccessfullyRun(codeBlock, outputFolder, rerun):

            # Save DEM to base folder as raw DEM with no compression
            pixelType = int(
                arcpy.GetRasterProperties_management(DEM,
                                                     "VALUETYPE").getOutput(0))

            if pixelType == 9:  # 32 bit float
                arcpy.CopyRaster_management(DEM,
                                            rawDEM,
                                            pixel_type="32_BIT_FLOAT")
            else:
                log.info("Converting DEM to 32 bit floating type")
                arcpy.CopyRaster_management(DEM, DEMTemp)
                arcpy.CopyRaster_management(Float(DEMTemp),
                                            rawDEM,
                                            pixel_type="32_BIT_FLOAT")

                # Delete temporary DEM
                arcpy.Delete_management(DEMTemp)

            # Calculate statistics for raw DEM
            arcpy.CalculateStatistics_management(rawDEM)

            progress.logProgress(codeBlock, outputFolder)

        ################################
        ### Create multiplier raster ###
        ################################

        codeBlock = 'Create multiplier raster'
        if not progress.codeSuccessfullyRun(codeBlock, outputFolder, rerun):

            Reclassify(rawDEM, "Value", RemapRange([[-999999.9, 999999.9, 1]]),
                       "NODATA").save(multRaster)
            progress.logProgress(codeBlock, outputFolder)

        codeBlock = 'Calculate slope in percent'
        if not progress.codeSuccessfullyRun(codeBlock, outputFolder, rerun):

            intSlopeRawPer = Slope(rawDEM, "PERCENT_RISE")
            intSlopeRawPer.save(slopeRawPer)
            del intSlopeRawPer

            log.info('Slope calculated in percent')

            progress.logProgress(codeBlock, outputFolder)

        if reconDEM is True:

            #######################
            ### Burn in streams ###
            #######################

            codeBlock = 'Burn in streams'
            if not progress.codeSuccessfullyRun(codeBlock, outputFolder,
                                                rerun):

                # Recondition DEM (burning stream network in using AGREE method)
                log.info("Burning streams into DEM.")
                reconditionDEM.function(rawDEM, streamInput, smoothDropBuffer,
                                        smoothDrop, streamDrop, burnedDEM)
                log.info("Completed stream network burn in to DEM")

                progress.logProgress(codeBlock, outputFolder)

            ##################
            ### Fill sinks ###
            ##################

            codeBlock = 'Fill sinks'
            if not progress.codeSuccessfullyRun(codeBlock, outputFolder,
                                                rerun):

                Fill(burnedDEM).save(hydDEM)

                log.info("Sinks in DEM filled")
                progress.logProgress(codeBlock, outputFolder)

            ######################
            ### Flow direction ###
            ######################

            codeBlock = 'Flow direction'
            if not progress.codeSuccessfullyRun(codeBlock, outputFolder,
                                                rerun):

                FlowDirection(hydDEM, "NORMAL").save(hydFDR)
                log.info("Flow Direction calculated")
                progress.logProgress(codeBlock, outputFolder)

            #################################
            ### Flow direction in degrees ###
            #################################

            codeBlock = 'Flow direction in degrees'
            if not progress.codeSuccessfullyRun(codeBlock, outputFolder,
                                                rerun):

                # Save flow direction raster in degrees (for display purposes)
                degreeValues = RemapValue([[1, 90], [2, 135], [4, 180],
                                           [8, 225], [16, 270], [32, 315],
                                           [64, 0], [128, 45]])
                Reclassify(hydFDR, "Value", degreeValues,
                           "NODATA").save(hydFDRDegrees)
                progress.logProgress(codeBlock, outputFolder)

            #########################
            ### Flow accumulation ###
            #########################

            codeBlock = 'Flow accumulation'
            if not progress.codeSuccessfullyRun(codeBlock, outputFolder,
                                                rerun):

                hydFACTemp = FlowAccumulation(hydFDR, "", "FLOAT")
                hydFACTemp.save(hydFAC)
                arcpy.sa.Int(Raster(hydFAC)).save(hydFACInt)  # integer version
                log.info("Flow Accumulation calculated")

                progress.logProgress(codeBlock, outputFolder)

            #######################
            ### Calculate slope ###
            #######################

            codeBlock = 'Calculate slope on burned DEM'
            if not progress.codeSuccessfullyRun(codeBlock, outputFolder,
                                                rerun):

                intSlopeHydDeg = Slope(hydDEM, "DEGREE")
                intSlopeHydDeg.save(slopeHydDeg)
                del intSlopeHydDeg

                intSlopeHydPer = Slope(hydDEM, "PERCENT_RISE")
                intSlopeHydPer.save(slopeHydPer)
                del intSlopeHydPer

                log.info('Slope calculated')

                progress.logProgress(codeBlock, outputFolder)

            ##########################
            ### Create stream file ###
            ##########################

            codeBlock = 'Create stream file'
            if not progress.codeSuccessfullyRun(codeBlock, outputFolder,
                                                rerun):

                # Create accumulation in metres
                streamAccHaFileInt = hydFACTemp * cellSizeDEM * cellSizeDEM / 10000.0
                streamAccHaFileInt.save(streamAccHaFile)
                del streamAccHaFileInt

                # Check stream initiation threshold reached
                streamYes = float(
                    arcpy.GetRasterProperties_management(
                        streamAccHaFile, "MAXIMUM").getOutput(0))

                if streamYes > float(minAccThresh):

                    reclassifyRanges = RemapRange(
                        [[-1000000, float(minAccThresh), 1],
                         [float(minAccThresh), 9999999999, 0]])

                    outLUCIstream = Reclassify(streamAccHaFile, "VALUE",
                                               reclassifyRanges)
                    outLUCIstream.save(streamInvRas)
                    del outLUCIstream
                    log.info("Stream raster for input to LUCI created")

                    # Create stream file for display
                    reclassifyRanges = RemapRange(
                        [[0, float(minAccThresh), "NODATA"],
                         [float(minAccThresh),
                          float(majAccThresh), 1],
                         [float(majAccThresh), 99999999999999, 2]])

                    streamsRaster = Reclassify(streamAccHaFile, "Value",
                                               reclassifyRanges, "NODATA")
                    streamOrderRaster = StreamOrder(streamsRaster, hydFDR,
                                                    "STRAHLER")
                    streamsRaster.save(streamsRasterFile)

                    # Create two streams feature classes - one for analysis and one for display
                    arcpy.sa.StreamToFeature(streamOrderRaster, hydFDR,
                                             streams, 'NO_SIMPLIFY')
                    arcpy.sa.StreamToFeature(streamOrderRaster, hydFDR,
                                             streamDisplay, 'SIMPLIFY')

                    # Rename grid_code column to 'Strahler'
                    for streamFC in [streams, streamDisplay]:

                        arcpy.AddField_management(streamFC, "Strahler", "LONG")
                        arcpy.CalculateField_management(
                            streamFC, "Strahler", "!GRID_CODE!", "PYTHON_9.3")
                        arcpy.DeleteField_management(streamFC, "GRID_CODE")

                    del streamsRaster
                    del streamOrderRaster

                    log.info("Stream files created")

                else:

                    warning = 'No streams initiated'
                    log.warning(warning)
                    common.logWarnings(outputFolder, warning)

                    # Create LUCIStream file from multiplier raster (i.e. all cells have value of 1 = no stream)
                    arcpy.CopyRaster_management(multRaster, streamInvRas)

                progress.logProgress(codeBlock, outputFolder)

        codeBlock = 'Clip data, build pyramids and generate statistics'
        if not progress.codeSuccessfullyRun(codeBlock, outputFolder, rerun):

            try:
                # Generate pyramids and stats
                arcpy.BuildPyramidsandStatistics_management(
                    outputFolder, "", "", "", "")
                log.info(
                    "Pyramids and Statistics calculated for all LUCI topographical information rasters"
                )

            except Exception:
                log.info("Warning - could not generate all raster statistics")

            progress.logProgress(codeBlock, outputFolder)

        # Reset snap raster
        arcpy.env.snapRaster = None

    except Exception:
        log.error("Error in preprocessing operations")
        raise
Beispiel #4
0
def function(outputFolder,
             preprocessFolder,
             lsOption,
             slopeAngle,
             soilOption,
             soilData,
             soilCode,
             lcOption,
             landCoverData,
             landCoverCode,
             rData,
             saveFactors,
             supportData,
             rerun=False):

    try:
        # Set temporary variables
        prefix = os.path.join(arcpy.env.scratchGDB, "rusle_")

        supportCopy = prefix + "supportCopy"
        soilResample = prefix + "soilResample"
        lcResample = prefix + "lcResample"
        rainResample = prefix + "rainResample"
        supportResample = prefix + "supportResample"
        soilClip = prefix + "soilClip"
        landCoverClip = prefix + "landCoverClip"
        rainClip = prefix + "rainClip"
        supportClip = prefix + "supportClip"
        DEMSlopeCut = prefix + "DEMSlopeCut"
        DEMSlopeRad = prefix + "DEMSlopeRad"
        upslopeArea = prefix + "upslopeArea"
        soilJoin = prefix + "soilJoin"
        lcJoin = prefix + "lcJoin"
        rFactor = prefix + "rFactor"
        lsFactor = prefix + "lsFactor"
        kFactor = prefix + "kFactor"
        cFactor = prefix + "cFactor"
        pFactor = prefix + "pFactor"
        soilLossInt = prefix + "soilLossInt"
        landCoverRas = prefix + "landCoverRas"
        soilRas = prefix + "soilRas"
        dataMask = prefix + "dataMask"

        # Get input study area mask
        files = common.getFilenames('preprocess', preprocessFolder)
        studyMask = files.studyareamask
        inputLC = files.lc_ras
        inputSoil = files.soil_ras
        DEMSlopePerc = files.slopeRawPer
        DEMSlope = files.slopeHydDeg
        hydFAC = files.hydFAC
        rawDEM = files.rawDEM
        streamInvRas = files.streamInvRas

        # Set output filenames
        files = common.getFilenames('rusle', outputFolder)
        soilLoss = files.soilloss

        if saveFactors:
            # if RUSLE factor layers are to be saved

            rFactor = files.rFactor
            lsFactor = files.lsFactor
            kFactor = files.kFactor
            cFactor = files.cFactor
            pFactor = files.pFactor

        reconOpt = common.getInputValue(preprocessFolder, 'Recondition_DEM')

        ####################
        ### Check inputs ###
        ####################

        codeBlock = 'Check if new inputs are in a projected coordinate systems'
        if not progress.codeSuccessfullyRun(codeBlock, outputFolder, rerun):

            inputs = [rData]

            optInputs = [soilData, landCoverData, supportData]
            for data in optInputs:
                if data is not None:
                    inputs.append(data)

            for data in inputs:
                spatialRef = arcpy.Describe(data).spatialReference

                if spatialRef.Type == "Geographic":
                    # If any of the inputs are not in a projected coordinate system, the tool exits with a warning
                    log.error('Data: ' + str(data))
                    log.error(
                        'This data has a Geographic Coordinate System. It must have a Projected Coordinate System.'
                    )
                    sys.exit()

            log.info(
                'All new inputs are in a projected coordinate system, proceeding.'
            )

            progress.logProgress(codeBlock, outputFolder)

        try:

            # Set environment and extents to DEM
            RawDEM = Raster(rawDEM)

            arcpy.env.extent = RawDEM
            arcpy.env.mask = RawDEM
            arcpy.env.cellSize = RawDEM
            arcpy.env.compression = "None"

            cellsizedem = float(
                arcpy.GetRasterProperties_management(rawDEM,
                                                     "CELLSIZEX").getOutput(0))

            log.info("Calculation extent set to DEM data extent")

        except Exception:
            log.error("Environment and extent conditions not set correctly")
            raise

        codeBlock = 'Convert any vector inputs to raster'
        if not progress.codeSuccessfullyRun(codeBlock, outputFolder, rerun):

            if landCoverData is not None:
                lcFormat = arcpy.Describe(landCoverData).dataType

                if lcFormat in ['ShapeFile', 'FeatureClass']:

                    arcpy.PolygonToRaster_conversion(landCoverData,
                                                     landCoverCode,
                                                     landCoverRas,
                                                     "CELL_CENTER", "",
                                                     cellsizedem)
                    log.info('Land cover raster produced')

                else:
                    arcpy.CopyRaster_management(landCoverData, landCoverRas)

            if soilData is not None:
                soilFormat = arcpy.Describe(soilData).dataType

                if soilFormat in ['ShapeFile', 'FeatureClass']:

                    arcpy.PolygonToRaster_conversion(soilData, soilCode,
                                                     soilRas, "CELL_CENTER",
                                                     "", cellsizedem)
                    log.info('Soil raster produced')

                else:
                    arcpy.CopyRaster_management(soilData, soilRas)

            progress.logProgress(codeBlock, outputFolder)

        codeBlock = 'Resample down to DEM cell size'
        if not progress.codeSuccessfullyRun(codeBlock, outputFolder, rerun):

            # Resample down to DEM cell size
            log.info("Resampling inputs down to DEM cell size")

            resampledRainTemp = arcpy.sa.ApplyEnvironment(rData)
            resampledRainTemp.save(rainResample)
            del resampledRainTemp

            if soilData is not None:
                resampledSoilTemp = arcpy.sa.ApplyEnvironment(soilRas)
                resampledSoilTemp.save(soilResample)
                del resampledSoilTemp

                # Delete soil raster
                arcpy.Delete_management(soilRas)

            if landCoverData is not None:
                resampledLCTemp = arcpy.sa.ApplyEnvironment(landCoverRas)
                resampledLCTemp.save(lcResample)
                del resampledLCTemp

                # Delete land cover raster
                arcpy.Delete_management(landCoverRas)

            if supportData is not None:

                arcpy.CopyRaster_management(supportData, supportCopy)
                resampledPTemp = arcpy.sa.ApplyEnvironment(supportCopy)
                resampledPTemp.save(supportResample)
                del resampledPTemp

                # Delete support raster
                arcpy.Delete_management(supportCopy)

            log.info("Inputs resampled")

            progress.logProgress(codeBlock, outputFolder)

        codeBlock = 'Clip inputs'
        if not progress.codeSuccessfullyRun(codeBlock, outputFolder, rerun):

            log.info("Clipping inputs")

            arcpy.Clip_management(rainResample,
                                  "#",
                                  rainClip,
                                  studyMask,
                                  clipping_geometry="ClippingGeometry")

            # Delete resampled R-factor
            arcpy.Delete_management(rainResample)

            if soilData is not None:
                arcpy.Clip_management(soilResample,
                                      "#",
                                      soilClip,
                                      studyMask,
                                      clipping_geometry="ClippingGeometry")

                # Delete resampled soil
                arcpy.Delete_management(soilResample)

            if landCoverData is not None:
                arcpy.Clip_management(lcResample,
                                      "#",
                                      landCoverClip,
                                      studyMask,
                                      clipping_geometry="ClippingGeometry")

                # Delete resampled land cover
                arcpy.Delete_management(lcResample)

            if supportData is not None:
                arcpy.Clip_management(supportResample,
                                      "#",
                                      supportClip,
                                      studyMask,
                                      clipping_geometry="ClippingGeometry")

                # Delete resampled support data
                arcpy.Delete_management(supportResample)

            log.info("Inputs clipped")

            progress.logProgress(codeBlock, outputFolder)

        codeBlock = 'Check against study area mask'
        if not progress.codeSuccessfullyRun(codeBlock, outputFolder, rerun):

            inputs = [rainClip]

            optInputs = [soilClip, landCoverClip, supportClip]
            for data in optInputs:
                if arcpy.Exists(data):
                    inputs.append(data)

            for data in inputs:
                dataMask = common.extractRasterMask(data)
                common.checkCoverage(dataMask, studyMask, data)
                del dataMask

            progress.logProgress(codeBlock, outputFolder)

        ####################################
        ### Rainfall factor calculations ###
        ####################################

        codeBlock = 'Produce R-factor layer'
        if not progress.codeSuccessfullyRun(codeBlock, outputFolder, rerun):

            # Copy resampled raster
            arcpy.CopyRaster_management(rainClip, rFactor)

            # Delete clipped R-factor
            arcpy.Delete_management(rainClip)

            log.info("R-factor layer produced")

            progress.logProgress(codeBlock, outputFolder)

        ######################################################
        ### Slope length and steepness factor calculations ###
        ######################################################

        codeBlock = 'Produce LS-factor layer'
        if not progress.codeSuccessfullyRun(codeBlock, outputFolder, rerun):

            # Calculate the threshold slope angle in percent
            lsFactorRad = float(slopeAngle) * (math.pi / 180.0)
            riseRun = math.tan(lsFactorRad)
            cutoffPercent = riseRun * 100.0

            if lsOption == 'SlopeLength':

                log.info(
                    "Calculating LS-factor based on slope length and steepness only"
                )

                # Produce slope cutoff raster
                DEMSlopeCutTemp = Con(
                    Raster(DEMSlopePerc) > float(cutoffPercent),
                    float(cutoffPercent), Raster(DEMSlopePerc))
                DEMSlopeCutTemp.save(DEMSlopeCut)
                del DEMSlopeCutTemp

                # Calculate the parts of the LS-factor equation separately
                lsCalcA = (cellsizedem / 22.0)**0.5
                lsCalcB = 0.065 + (0.045 * Raster(DEMSlopeCut)) + (
                    0.0065 * Power(Raster(DEMSlopeCut), 2.0))

                # Calculate the LS-factor
                lsOrigTemp = lsCalcA * lsCalcB
                lsOrigTemp.save(lsFactor)

                # Delete temporary files
                del lsCalcB
                del lsOrigTemp
                arcpy.Delete_management(DEMSlopeCut)

                log.info("LS-factor layer produced")

            elif lsOption == 'UpslopeArea':

                if reconOpt == 'false':
                    log.error(
                        'Cannot calculate LS-factor including upslope contributing area on unreconditioned DEM'
                    )
                    log.error(
                        'Rerun the preprocessing tool to recondition the DEM')
                    sys.exit()

                log.info(
                    "Calculating LS-factor including upslope contributing area"
                )

                # Produce slope cutoff raster
                DEMSlopeCutTemp = Con(
                    Raster(DEMSlope) > float(slopeAngle), float(slopeAngle),
                    Raster(DEMSlope))
                DEMSlopeCutTemp.save(DEMSlopeCut)
                del DEMSlopeCutTemp

                # Convert from degrees to radian
                DEMSlopeRadTemp = Raster(DEMSlopeCut) * 0.01745
                DEMSlopeRadTemp.save(DEMSlopeRad)
                del DEMSlopeRadTemp

                # Currently hardcoded, but should have them as options in future
                m = 0.5
                n = 1.2

                upslopeAreaTemp = Raster(hydFAC) * float(cellsizedem)
                upslopeAreaTemp.save(upslopeArea)
                del upslopeAreaTemp

                lsFactorTemp = (m + 1) * Power(
                    Raster(upslopeArea) / 22.1, float(m)) * Power(
                        Sin(Raster(DEMSlopeRad)) / 0.09, float(n))
                lsFactorTemp.save(lsFactor)
                del lsFactorTemp

                # Delete temporary files
                arcpy.Delete_management(DEMSlopeCut)
                arcpy.Delete_management(DEMSlopeRad)
                arcpy.Delete_management(upslopeArea)

                log.info("LS-factor layer produced")

            progress.logProgress(codeBlock, outputFolder)

        ################################
        ### Soil factor calculations ###
        ################################

        codeBlock = 'Produce K-factor layer'
        if not progress.codeSuccessfullyRun(codeBlock, outputFolder, rerun):

            if soilOption == 'PreprocessSoil':

                # Use the soil from the preprocessFolder
                arcpy.CopyRaster_management(inputSoil, soilClip)

                kTable = os.path.join(configuration.tablesPath,
                                      "rusle_hwsd.dbf")
                arcpy.JoinField_management(soilClip, "VALUE", kTable,
                                           "MU_GLOBAL")
                arcpy.CopyRaster_management(soilClip, soilJoin)

                # Delete temporary files
                arcpy.Delete_management(soilClip)

                kOrigTemp = Lookup(soilJoin, "K_Stewart")
                kOrigTemp.save(kFactor)

                # Delete temporary files
                del kOrigTemp
                arcpy.Delete_management(soilJoin)

            elif soilOption == 'LocalSoil':

                # User input is their own K-factor dataset
                kOrigTemp = Raster(soilClip)
                kOrigTemp.save(kFactor)

                # Delete temporary files
                del kOrigTemp
                arcpy.Delete_management(soilClip)

            else:
                log.error('Invalid soil erodibility option')
                sys.exit()

            log.info("K-factor layer produced")

            progress.logProgress(codeBlock, outputFolder)

        #################################
        ### Cover factor calculations ###
        #################################

        codeBlock = 'Produce C-factor layer'
        if not progress.codeSuccessfullyRun(codeBlock, outputFolder, rerun):

            if lcOption == 'PrerocessLC':

                # Use LC from the preprocess folder

                arcpy.CopyRaster_management(inputLC, landCoverClip)

                cTable = os.path.join(configuration.tablesPath,
                                      "rusle_esacci.dbf")

                arcpy.JoinField_management(landCoverClip, "VALUE", cTable,
                                           "LC_CODE")
                arcpy.CopyRaster_management(landCoverClip, lcJoin)

                arcpy.Delete_management(landCoverClip)

                cOrigTemp = Lookup(lcJoin, "CFACTOR")
                cOrigTemp.save(cFactor)

                # Delete temporary files
                del cOrigTemp
                arcpy.Delete_management(lcJoin)

            elif lcOption == 'LocalCfactor':

                # User input is their own C-factor dataset

                cOrigTemp = Raster(landCoverClip)
                cOrigTemp.save(cFactor)

                # Delete temporary files
                del cOrigTemp
                arcpy.Delete_management(landCoverClip)

            else:
                log.error('Invalid C-factor option')
                sys.exit()

            log.info("C-factor layer produced")

            progress.logProgress(codeBlock, outputFolder)

        #####################################
        ### Support practice calculations ###
        #####################################

        codeBlock = 'Produce P-factor layer'
        if not progress.codeSuccessfullyRun(codeBlock, outputFolder, rerun):

            if supportData is not None:
                arcpy.CopyRaster_management(supportClip, pFactor)
                log.info("P-factor layer produced")

                # Delete temporary files
                arcpy.Delete_management(supportClip)

            progress.logProgress(codeBlock, outputFolder)

        ##############################
        ### Soil loss calculations ###
        ##############################

        codeBlock = 'Produce soil loss layer'
        if not progress.codeSuccessfullyRun(codeBlock, outputFolder, rerun):

            if supportData is not None:
                soilLossTemp = Raster(rFactor) * Raster(lsFactor) * Raster(
                    kFactor) * Raster(cFactor) * Raster(pFactor)

            else:
                soilLossTemp = Raster(rFactor) * Raster(lsFactor) * Raster(
                    kFactor) * Raster(cFactor)

            if lsOption == 'UpslopeArea':
                soilLossTemp = soilLossTemp * Raster(streamInvRas)
                soilLossTemp.save(soilLoss)

            else:
                soilLossTemp.save(soilLoss)

            log.info("RUSLE function completed successfully")

            progress.logProgress(codeBlock, outputFolder)

        return soilLoss

    except Exception:
        arcpy.AddError("RUSLE function failed")
        raise

    finally:
        # Remove feature layers from memory
        try:
            for lyr in common.listFeatureLayers(locals()):
                arcpy.Delete_management(locals()[lyr])
                exec(lyr + ' = None') in locals()
        except Exception:
            pass