Exemple #1
0
def run_analysis():
    """Run Climate Linkage Mapper analysis."""
    import cc_grass_cwd  # Cannot import until configured

    cc_copy_inputs()  # Clip inputs and create project area raster

    # Get zonal statistics for cores and climate
    lm_util.gprint("\nCALCULATING ZONAL STATISTICS FROM CLIMATE RASTER")
    climate_stats = arcpy.sa.ZonalStatisticsAsTable(cc_env.prj_core_fc,
                                                    cc_env.core_fld,
                                                    cc_env.prj_climate_rast,
                                                    "zstats", "DATA", "ALL")

    # Create core pairings table and limit based upon climate threshold
    core_pairings = create_pair_tbl(climate_stats)

    # Generate link table, calculate CWD and run Linkage Mapper
    if int(arcpy.GetCount_management(core_pairings).getOutput(0)) == 0:
        lm_util.warn("\nNo core pairs within climate threshold. "
                     "Program will end")
    else:
        # Process pairings and generate link table
        grass_cores = process_pairings(core_pairings)
        if not grass_cores:
            lm_util.warn("\nNo core pairs within Euclidean distances. "
                         "Progam will end")
        else:
            # Create CWD using Grass
            cc_grass_cwd.grass_cwd(grass_cores)
            # Run Linkage Mapper
            lm_util.gprint("\nRUNNING LINKAGE MAPPER "
                           "TO CREATE CLIMATE CORRIDORS")
            lm_master.lm_master()
Exemple #2
0
def run_analysis():
    """Run Climate Linkage Mapper analysis"""
    import cc_grass_cwd  # Cannot import until configured

    zonal_tbl = "zstats.dbf"

    cc_copy_inputs()  # Clip inputs and create project area raster

    # Get zonal statistics for cores and climate
    lm_util.gprint("\nCALCULATING ZONAL STATISTICS FROM CLIMATE RASTER")
    climate_stats = arcpy.sa.ZonalStatisticsAsTable(
        cc_env.prj_core_fc, cc_env.core_fld, cc_env.prj_climate_rast,
        zonal_tbl, "DATA", "ALL")

    # Create core pairings table and limit based upon climate threshold
    core_pairings = create_pair_tbl(climate_stats)

    # Generate link table, calculate CWD and run Linkage Mapper
    if int(arcpy.GetCount_management(core_pairings).getOutput(0)) == 0:
        arcpy.AddWarning("\nNo core pairs within climate threshold. "
                         "Program will end")
    else:
        # Process pairings and generate link table
        grass_cores = process_pairings(core_pairings)
        if not grass_cores:
            arcpy.AddWarning("\nNo core pairs within Euclidean distances. "
                             "Progam program will end")
        else:
            # Create CWD using Grass
            cc_grass_cwd.grass_cwd(grass_cores)
            # Run Linkage Mapper
            lm_util.gprint("\nRUNNING LINKAGE MAPPER "
                           "TO CREATE CLIMATE CORRIDORS")
            lm_master.lm_master()
Exemple #3
0
def main():
    """Set path and run model."""
    demo_path = (os.path.abspath(
        os.path.join(os.path.dirname(os.path.abspath(__file__)), '..')))

    sys.path.append(os.path.join(demo_path, '..\\toolbox\\scripts'))
    import lm_master
    lm_master.lm_master(in_params(demo_path))
Exemple #4
0
def main():
    """Runs Linkage Mapper with coded inputs"""
    proj_dir = "V:\\demoProject"
    core_fc = "V:\\demoData\\Cores.shp"
    core_fl = "core_ID"
    resis_rast = "V:\\demoData\\resistances"
    # distance_file = "V:\\demoData\\distances_Cores.txt"

    arg = (_SCRIPT_NAME, proj_dir, core_fc, core_fl, resis_rast, "true",
           "true", "Cost-Weighted & Euclidean", "#", "true", "true", "false",
           "4", "Cost-Weighted", "false", "true", "100000", "100000", "100000")
    sys.argv = arg

    sys.path.append('../toolbox/scripts')
    import lm_master
    lm_master.lm_master()
Exemple #5
0
def main():
    """Runs Linkage Mapper with coded inputs"""
    proj_dir = "V:\\demoProject"
    core_fc = "V:\\demoData\\Cores.shp"
    core_fl = "core_ID"
    resis_rast = "V:\\demoData\\resistances"
    # distance_file = "V:\\demoData\\distances_Cores.txt"

    arg = (_SCRIPT_NAME, proj_dir, core_fc, core_fl, resis_rast, "true", "true",
            "Cost-Weighted & Euclidean", "#",
            "true", "true", "false", "4", "Cost-Weighted", "false", "true",
            "100000", "100000", "100000")
    sys.argv = arg

    sys.path.append('../toolbox/scripts')
    import lm_master
    lm_master.lm_master()
Exemple #6
0
def main(argv=None):
    """Iterate over LM, BM, and restoration tasks."""
    if argv is None:
        argv = sys.argv  # Get parameters from ArcGIS tool dialog

    start_time = time.clock()

    # USER SETTINGS ######################################################

    # Restoration Settings
    # ALL input data must be in the same projection

    # Set to True to restore highest ROI. Set to False to restore strongest
    # barrier
    restore_max_roi = argv[1]

    # Resistance value of restored habitat.  Must be 1 or greater.
    restored_resistance_val = argv[2]

    # No spaces or special chars in paths or gdb names
    restoration_data_gdb = argv[3]

    # No spaces in path, avoid using dropbox or network drive
    # Project directories will be created in this (iter1, iter2...) as will an
    # output geodatabase
    output_dir = argv[4]

    # Resistance raster. Should be in input GDB
    resistance_ras = argv[5]
    # Core area feature class. Should be in input GDB 'URWA_HCAs_Doug_Grant'
    core_fc = argv[6]

    core_fn = argv[7]  # Core area field name

    radius = argv[8]  # Restoration radius in meters
    iterations = argv[9]  # Number of restorations to perform

    # If less than this proportion of ag in circle, don't consider restoring
    # circle
    min_ag_threshold = argv[10]

    # Don't consider barriers below this improvement score (average improvement
    # per meter diameter restored)
    min_improvement_val = argv[11]

    # Average per-m2 parcel cost per pixel. Snapped to resistance raster.
    parcel_cost_ras = argv[12]

    # Right now this is just a raster with all pixels set to 0.113174
    restoration_cost_ras = argv[13]

    ag_ras = argv[14]  # 1=Ag, 0=Not Ag

    # Some restorations benefit multiple corridors.
    # 'Maximum' takes the greatest improvement across core area pairs
    # 'Sum' adds improvement scores acreoss all pairs.
    barrier_combine_method = argv[15]

    # Use cwd_thresh = None for no threshold. Use cwd_thresh = X to not
    # consider restorations more than X map units away from each core area.
    cwd_thresh = argv[16]

    # END USER SETTINGS ######################################################

    try:
        # Setup path and create directories
        gprint('Hey! Make sure everything is in the same projection!\n')
        gprint('Setting up paths and creating directories')
        sys.path.append('..\\toolbox\\scripts')
        res_ras = os.path.join(restoration_data_gdb, resistance_ras)
        core_fc_path = os.path.join(restoration_data_gdb, core_fc)

        # Set up a NEW output gdb (leave previous ones on drive)
        i = None
        for i in range(1, 200):
            output_gdb = 'restorationOutput' + str(i) + '.gdb'
            if not arcpy.Exists(os.path.join(output_dir, output_gdb)):
                break
            gprint('Previous output GDB ' + output_gdb + ' exists.  '
                   'Delete to save disk space.')
        arcpy.CreateFileGDB_management(output_dir, output_gdb)
        output_gdb = os.path.join(output_dir, output_gdb)
        log_file = os.path.join(output_gdb,
                                'Iterate Barriers' + str(i) + '.py')

        # Write a copy of this file to output dir as a record of settings
        shutil.copyfile(__file__, log_file)

        arcpy.env.cellSize = res_ras
        arcpy.env.extent = res_ras
        arcpy.env.snapRaster = res_ras
        arcpy.env.overwriteOutput = True
        arcpy.env.scratchWorkspace = output_gdb
        arcpy.env.workspace = output_gdb

        spatialref = arcpy.Describe(res_ras).spatialReference
        mapunits = spatialref.linearUnitName
        gprint('Cell size = ' + str(arcpy.env.cellSize) + ' ' + mapunits + 's')

        # Calculate fraction of ag within radius of each pixel
        gprint('Calculating purchase cost, fraction of ag, etc within radius '
               'of each pixel.')
        ag_ras = os.path.join(restoration_data_gdb, ag_ras)
        in_neighborhood = arcpy.sa.NbrCircle(radius, "MAP")
        arcpy.env.extent = ag_ras
        out_focal_stats = arcpy.sa.FocalStatistics(ag_ras, in_neighborhood,
                                                   "MEAN", "NODATA")
        proportion_ag_ras = os.path.join(output_gdb, 'proportionAgRas')
        out_focal_stats.save(proportion_ag_ras)
        arcpy.env.extent = res_ras

        # Calculate purchase cost of circles
        parcel_cost_ras = os.path.join(restoration_data_gdb, parcel_cost_ras)
        arcpy.env.extent = parcel_cost_ras
        out_focal_stats = arcpy.sa.FocalStatistics(parcel_cost_ras,
                                                   in_neighborhood, "MEAN",
                                                   "DATA")
        cost_focal_stats_ras = os.path.join(output_gdb, 'cost_focal_stats_ras')
        out_focal_stats.save(cost_focal_stats_ras)
        arcpy.env.extent = res_ras

        circle_area = float(npy.pi * radius * radius)
        outras = arcpy.sa.Raster(cost_focal_stats_ras) * circle_area
        purch_cost_ras = os.path.join(output_gdb, 'purchaseCostRaster')
        outras.save(purch_cost_ras)
        lu.delete_data(cost_focal_stats_ras)

        restoration_cost_ras = os.path.join(restoration_data_gdb,
                                            restoration_cost_ras)
        outras = (
            arcpy.sa.Raster(purch_cost_ras) +
            (arcpy.sa.Raster(restoration_cost_ras) * radius * radius * npy.pi))
        total_cost_ras = os.path.join(output_gdb, 'totalCostRaster')
        outras.save(total_cost_ras)

        # Create mask to remove areas without cost data
        arcpy.env.extent = total_cost_ras
        cost_mask_ras = os.path.join(output_gdb, 'costMaskRaster')
        cost_thresh = 0
        out_con = arcpy.sa.Con(
            (arcpy.sa.Raster(total_cost_ras) > float(cost_thresh)), 1)
        out_con.save(cost_mask_ras)
        arcpy.env.extent = res_ras

        # Create mask to remove areas below ag threshold
        out_con = arcpy.sa.Con(
            (arcpy.sa.Raster(proportion_ag_ras) > float(min_ag_threshold)), 1)
        ag_mask_ras = os.path.join(output_gdb, 'agMaskRaster')
        out_con.save(ag_mask_ras)

        do_step_1 = 'true'
        do_step_2 = 'true'
        do_step_5 = 'false'
        all_restored_areas_ras = ''

        for cur_iter in range(1, iterations + 1):
            start_time1 = time.clock()

            # Some env settings get changed by linkage mapper and must be
            # reset here
            arcpy.env.cellSize = res_ras
            arcpy.env.extent = res_ras
            arcpy.env.snapRaster = res_ras
            arcpy.env.scratchWorkspace = output_gdb
            arcpy.env.workspace = output_gdb

            lu.dashline(1)
            gprint('Running iteration number ' + str(cur_iter))
            proj_dir = os.path.join(output_dir,
                                    'iter' + str(cur_iter) + 'Proj')
            lu.create_dir(output_dir)
            lu.delete_dir(proj_dir)
            lu.create_dir(proj_dir)
            if cur_iter > 1:  # Copy previous s2 linktable to new project dir
                datapass_dir = os.path.join(proj_dir, 'datapass')
                lu.create_dir(datapass_dir)
                proj_dir1 = os.path.join(output_dir, 'iter1Proj')
                datapass_dir_iter1 = os.path.join(proj_dir1, 'datapass')
                s2_link_tbl_iter1 = os.path.join(datapass_dir_iter1,
                                                 'linkTable_s2.csv')
                s2_link_tbl = os.path.join(datapass_dir, 'linkTable_s2.csv')
                shutil.copyfile(s2_link_tbl_iter1, s2_link_tbl)

            # Run Linkage Mapper

            # Copy distances text file from earlier LM run to the output
            # directory- speeds things up!
            dist_file = os.path.join(output_dir, core_fc + '_dists.txt')

            if not os.path.exists(dist_file):
                if cur_iter == 1:
                    gprint('Will calculate distance file.')
                    dist_file = '#'
                else:
                    proj_dir1 = os.path.join(output_dir, 'iter1Proj')
                    dist_file1 = os.path.join(proj_dir1,
                                              core_fc + '_dists.txt')
                    # Put a copy here for future runs
                    shutil.copyfile(dist_file1, dist_file)

            arcpy.env.scratchWorkspace = output_gdb
            arcpy.env.workspace = output_gdb

            argv = ('lm_master.py', proj_dir, core_fc_path, core_fn, res_ras,
                    do_step_1, do_step_2, 'Cost-Weighted & Euclidean',
                    dist_file, 'true', 'true', 'false', '4', 'Cost-Weighted',
                    'true', do_step_5, 'true', '200000', '10000', '#', '#',
                    '#', '#')
            gprint('Running ' + str(argv))
            lm_master.lm_master(argv)
            do_step_1 = 'false'  # Can skip for future iterations
            do_step_2 = 'false'  # Can skip for future iterations
            do_step_5 = 'false'  # Skipping for future iterations

            start_radius = str(radius)
            end_radius = str(radius)
            radius_step = '0'
            save_radius_ras = 'false'
            write_pct_ras = 'false'

            argv = ('barrier_master.py', proj_dir, res_ras, start_radius,
                    end_radius, radius_step, barrier_combine_method,
                    save_radius_ras, write_pct_ras, cwd_thresh)
            gprint('Running ' + str(argv))
            barrier_master.bar_master(argv)

            # Some env settings get changed by linkage mapper and must be
            # reset here
            arcpy.env.cellSize = res_ras
            arcpy.env.extent = res_ras
            arcpy.env.snapRaster = res_ras
            arcpy.env.scratchWorkspace = output_gdb
            arcpy.env.workspace = output_gdb

            gprint('Finding restoration circles with max barrier score / ROI')
            # Find points with max ROI
            prefix = os.path.basename(proj_dir)
            if barrier_combine_method == 'Sum':
                sum_suffix = 'Sum'
            else:
                sum_suffix = ''
            barrier_fn = (prefix + "_BarrierCenters" + sum_suffix + "_Rad" +
                          str(radius))
            barrier_ras = os.path.join(proj_dir, 'output', 'barriers.gdb',
                                       barrier_fn)
            if not arcpy.Exists(barrier_ras):
                msg = ('Error: cannot find barrier output: ' + barrier_ras)
                lu.raise_error(msg)

            if cur_iter > 1:
                gprint('Creating mask for previously restored areas')
                in_neighborhood = arcpy.sa.NbrCircle(radius, "MAP")
                arcpy.env.extent = all_restored_areas_ras
                out_focal_stats = arcpy.sa.FocalStatistics(
                    all_restored_areas_ras, in_neighborhood, "MEAN", "DATA")
                all_restored_focal_ras = os.path.join(
                    output_gdb, 'allRestFocRas_iter' + str(cur_iter))

                # Anything > 0 would include a restored area
                out_focal_stats.save(all_restored_focal_ras)
                arcpy.env.extent = res_ras
                rest_mask_ras = os.path.join(
                    output_gdb, 'restMaskRaster_iter' + str(cur_iter))
                minval = 0
                out_con = arcpy.sa.Con(
                    (arcpy.sa.Raster(all_restored_focal_ras) == float(minval)),
                    1)
                out_con.save(rest_mask_ras)

            # Candidate areas have not been restored, have cost data, meet
            # minimum improvement score criteria, and have enough ag in them
            candidate_barrier_ras = os.path.join(
                output_gdb, 'candidateBarrierRaster' + '_iter' + str(cur_iter))
            if cur_iter > 1:
                gprint('Creating candidate restoration raster using barrier '
                       'results, previous restorations, and selection '
                       'criteria')

                # ROI scores will be in terms of total improvement
                # (= score * diameter)
                out_calc = (arcpy.sa.Raster(cost_mask_ras) *
                            arcpy.sa.Raster(ag_mask_ras) *
                            arcpy.sa.Raster(barrier_ras) *
                            arcpy.sa.Raster(rest_mask_ras) * (radius * 2))
            else:
                out_calc = (arcpy.sa.Raster(cost_mask_ras) *
                            arcpy.sa.Raster(ag_mask_ras) *
                            arcpy.sa.Raster(barrier_ras) * radius * 2)

            min_barrier_score = min_improvement_val * radius * 2
            if restored_resistance_val != 1:
                out_calc_2 = (out_calc - (2 * radius *
                                          (restored_resistance_val - 1)))
                out_con = arcpy.sa.Con(
                    (out_calc_2 >= float(min_barrier_score)), out_calc_2)
            else:
                out_con = arcpy.sa.Con((out_calc >= float(min_barrier_score)),
                                       out_calc)
            out_con.save(candidate_barrier_ras)
            lu.build_stats(candidate_barrier_ras)

            purchase_roi_ras = os.path.join(
                output_gdb, 'purchaseRoiRaster' + '_iter' + str(cur_iter))
            out_calc = (arcpy.sa.Raster(candidate_barrier_ras) /
                        arcpy.sa.Raster(purch_cost_ras))
            out_calc.save(purchase_roi_ras)
            lu.build_stats(purchase_roi_ras)

            total_roi_ras = os.path.join(
                output_gdb, 'purchaseRestRoiRaster' + '_iter' + str(cur_iter))
            out_calc = (arcpy.sa.Raster(candidate_barrier_ras) /
                        arcpy.sa.Raster(total_cost_ras))
            out_calc.save(total_roi_ras)
            lu.build_stats(total_roi_ras)

            max_barrier = float(
                arcpy.GetRasterProperties_management(candidate_barrier_ras,
                                                     "MAXIMUM").getOutput(0))
            gprint('Maximum barrier improvement score: ' + str(max_barrier))
            if max_barrier < 0:
                arcpy.AddWarning("\nNo barriers found that meet CWD or Ag "
                                 "threshold criteria.")

            max_purch_roi = arcpy.GetRasterProperties_management(
                purchase_roi_ras, "MAXIMUM")
            gprint('Maximum purchase ROI score: ' +
                   str(max_purch_roi.getOutput(0)))

            max_roi = arcpy.GetRasterProperties_management(
                total_roi_ras, "MAXIMUM")
            gprint('Maximum total ROI score: ' + str(max_roi.getOutput(0)))

            if restore_max_roi:
                out_point = os.path.join(
                    output_gdb, 'maxRoiPoint' + '_iter' + str(cur_iter))
                gprint('Choosing circle with maximum ROI to restore')
                out_con = arcpy.sa.Con(
                    (arcpy.sa.Raster(total_roi_ras) >= float(
                        max_roi.getOutput(0))), total_roi_ras)
                max_roi_ras = os.path.join(output_gdb, 'max_roi_ras')
                out_con.save(max_roi_ras)
                # Save max ROI to point
                try:
                    arcpy.RasterToPoint_conversion(max_roi_ras, out_point)
                except Exception:
                    msg = ('Error: it looks like there are no viable '
                           'restoration candidates.')
                    lu.raise_error(msg)

            else:  # Restoring strongest barrier instead
                out_point = os.path.join(
                    output_gdb, 'maxBarrierPoint' + '_iter' + str(cur_iter))
                gprint('Choosing circle with maximum BARRIER IMPROVEMENT SCORE'
                       ' to restore')
                out_con = arcpy.sa.Con(
                    (arcpy.sa.Raster(candidate_barrier_ras) >= max_barrier),
                    candidate_barrier_ras)
                max_barrier_ras = os.path.join(output_gdb, 'maxBarrierRaster')
                out_con.save(max_barrier_ras)
                # Save max barrier to point
                try:
                    arcpy.RasterToPoint_conversion(max_barrier_ras, out_point)
                except Exception:
                    msg = ('Error: it looks like there are no viable '
                           'restoration candidates.')
                    lu.raise_error(msg)

            gprint('Done evaluating candidate restorations')
            result = int(arcpy.GetCount_management(out_point).getOutput(0))
            if result > 1:
                # Would be better to retain point with max barrier score when
                # we have multiple points with same ROI
                arcpy.AddWarning('Deleting points with identical '
                                 'ROI/improvement score values')

                arcpy.DeleteIdentical_management(out_point, "grid_code", 0.1,
                                                 0.1)

            arcpy.sa.ExtractMultiValuesToPoints(
                out_point,
                [[candidate_barrier_ras, "barrierScore"],
                 [purch_cost_ras, "purchCost"], [total_cost_ras, "totalCost"],
                 [purchase_roi_ras, "purchaseROI"],
                 [total_roi_ras, "totalROI"]], "NONE")

            arcpy.AddField_management(out_point, "restorationNumber", "SHORT")
            arcpy.CalculateField_management(out_point, "restorationNumber",
                                            cur_iter, "PYTHON_9.3")
            arcpy.AddField_management(out_point, "radius", "DOUBLE")
            arcpy.CalculateField_management(out_point, "radius", radius,
                                            "PYTHON_9.3")
            arcpy.AddField_management(out_point, "barrierScore_per_m",
                                      "DOUBLE")
            arcpy.CalculateField_management(
                out_point, "barrierScore_per_m",
                "(float(!barrierScore!) / (!radius! * 2))", "PYTHON_9.3")

            gprint('\nCreating restoration circles')
            if restore_max_roi:
                circle_fc = os.path.join(
                    output_gdb, 'maxRoiCircle' + '_iter' + str(cur_iter))
            else:
                circle_fc = os.path.join(
                    output_gdb, 'maxBarrierCircle' + '_iter' + str(cur_iter))
            arcpy.Buffer_analysis(out_point, circle_fc, radius)
            gprint('Rasterizing restoration circles')
            if restore_max_roi:
                circle_ras = os.path.join(
                    output_gdb, 'maxRoicircle_ras' + '_iter' + str(cur_iter))
            else:
                circle_ras = os.path.join(
                    output_gdb,
                    'maxBarrierCircleRas' + '_iter' + str(cur_iter))
            arcpy.FeatureToRaster_conversion(circle_fc, 'totalROI', circle_ras,
                                             arcpy.env.cellSize)

            # restore raster
            gprint('Digitally restoring resistance raster')
            res_ras_restored = os.path.join(
                output_gdb, 'resRastRestored' + '_iter' + str(cur_iter))
            out_con = arcpy.sa.Con(arcpy.sa.IsNull(circle_ras), res_ras,
                                   restored_resistance_val)
            out_con.save(res_ras_restored)

            all_restored_areas_ras = os.path.join(
                output_gdb, 'allRestoredAreas_iter' + str(cur_iter))
            prev_restored_areas_ras = os.path.join(
                output_gdb, 'allRestoredAreas_iter' + str(cur_iter - 1))
            if cur_iter == 1:
                out_con = arcpy.sa.Con(arcpy.sa.IsNull(circle_ras), 0, 1)
            else:
                # Add this restoration to areas restored
                out_con = arcpy.sa.Con(arcpy.sa.IsNull(circle_ras),
                                       prev_restored_areas_ras, 1)
            out_con.save(all_restored_areas_ras)

            lu.delete_data(circle_ras)

            # Use for next iteration resistance raster
            res_ras = res_ras_restored

            # Add circle into feature class with all circles
            if restore_max_roi:
                all_circles_fc = os.path.join(output_gdb, "allCirclesMaxROI")
            else:
                all_circles_fc = os.path.join(output_gdb,
                                              "allCirclesMaxBarriers")
            if cur_iter == 1:
                arcpy.CopyFeatures_management(circle_fc, all_circles_fc)
            else:
                arcpy.Append_management(circle_fc, all_circles_fc, "TEST")
            gprint('Finished iteration #' + str(cur_iter))
            start_time1 = lu.elapsed_time(start_time1)

        gprint('\nDone with iterations.')
        start_time = lu.elapsed_time(start_time)
        gprint('Outputs saved in: ' + output_gdb)
        gprint('Back up your project directories if you want to save '
               'corridor/barrier results.')

    # Return GEOPROCESSING specific errors
    except arcpy.ExecuteError:
        lu.dashline(1)
        gprint('****Iteration script failed. Details follow.****')
        lu.exit_with_geoproc_error(_SCRIPT_NAME)

    # Return any PYTHON or system specific errors
    except Exception:
        lu.dashline(1)
        gprint('****Iteration script failed. Details follow.****')
        lu.exit_with_python_error(_SCRIPT_NAME)
def main():
    """Iterates over LM, BM, and restoration tasks"""

    ## USER SETTINGS ######################################################
    ## Restoration Settings
    ## ALL input data must be in the same projection
    start_time = time.clock()
    restoreMaxROI = False  # Set to True to restore highest ROI
    # Set to False to restore strongest barrier
    restoredResistanceVal = 1  # Resistance value of restored habitat.  Must be 1 or greater.
    restorationDataGDB = (
        "C:\\barrierClassAnalysis\\RestorationINPUTS_July2013.gdb"
    )  # No spaces or special chars in paths or gdb names
    outputDir = "C:\\barrierClassAnalysis\\output"  # No spaces in path, avoid using dropbox or network drive
    # Project directories will be created in this (iter1, iter2...)
    # as will an output geodatabase
    resistanceRaster = "URWA_resis"  # Resistance raster.  Should be in input GDB
    coreFC = "URWA_HCAs_Doug_Grant"  # Core area feature class. Should be in input GDB 'URWA_HCAs_Doug_Grant'
    coreFN = "HCA_ID"  # Core area field name

    radius = 450  # restoration radius in meters
    iterations = 13  # number of restorations to perform
    minAgThreshold = 0.75  # if less than this proportion of ag in circle, don't consider restoring circle
    minImprovementVal = (
        0
    )  # Don't consider barriers below this improvement score (average improvement per meter diameter restored)
    parcelCostRaster = (
        "DougGrantParcelCost_m2_projected_90m"
    )  # Average per-m2 parcel cost per pixel. Snapped to resistance raster.
    restorationCostRaster = "restCostPer_m2"  # Right now this is just a raster with all pixels set to 0.113174
    agRaster = "ARESmaskp_projected"  # 1=Ag, 0 = not Ag
    barrierCombineMethod = "Maximum"  # Some restorations benefit multiple corridors.
    # 'Maximum' takes the greatest improvement across core area pairs
    # 'Sum' adds improvement scores acreoss all pairs.
    cwdThresh = None  # Use cwdThresh = None for no threshold. Use cwdThresh = X to not consider
    # restorations more than X map units away from each core area.
    ## END USER SETTINGS ######################################################
    try:
        # Setup path and create directories
        gprint("Hey! Make sure everything is in the same projection!\n")
        gprint("Setting up paths and creating directories")
        sys.path.append("..\\toolbox\\scripts")
        resRast = os.path.join(restorationDataGDB, resistanceRaster)
        coreFCPath = os.path.join(restorationDataGDB, coreFC)

        # Set up a NEW output gdb (leave previous ones on drive)
        for i in range(1, 200):
            outputGDB = "restorationOutput" + str(i) + ".gdb"
            if not arcpy.Exists(os.path.join(outputDir, outputGDB)):
                break
            gprint("Previous output GDB " + outputGDB + " exists.  Delete to save disk space.")
        arcpy.CreateFileGDB_management(outputDir, outputGDB)
        outputGDB = os.path.join(outputDir, outputGDB)
        logFile = os.path.join(outputGDB, "Iterate Barriers" + str(i) + ".py")
        shutil.copyfile(__file__, logFile)  # write a copy of this file to output dir as a record of settings

        arcpy.env.cellSize = resRast
        arcpy.env.extent = resRast
        arcpy.env.snapRaster = resRast
        arcpy.env.overwriteOutput = True
        arcpy.env.scratchWorkspace = outputGDB
        arcpy.env.workspace = outputGDB

        spatialref = arcpy.Describe(resRast).spatialReference
        mapunits = spatialref.linearUnitName
        gprint("Cell size = " + str(arcpy.env.cellSize) + " " + mapunits + "s")

        # Calculate fraction of ag within radius of each pixel
        gprint("Calculating purchase cost, fraction of ag, etc within radius of each pixel.")
        agRaster = os.path.join(restorationDataGDB, agRaster)
        inNeighborhood = NbrCircle(radius, "MAP")
        arcpy.env.extent = agRaster
        outFocalStats = arcpy.sa.FocalStatistics(agRaster, inNeighborhood, "MEAN", "NODATA")
        proportionAgRaster = os.path.join(outputGDB, "proportionAgRas")
        outFocalStats.save(proportionAgRaster)
        arcpy.env.extent = resRast

        # Calculate purchase cost of circles
        parcelCostRaster = os.path.join(restorationDataGDB, parcelCostRaster)
        arcpy.env.extent = parcelCostRaster
        outFocalStats = arcpy.sa.FocalStatistics(parcelCostRaster, inNeighborhood, "MEAN", "DATA")
        costFocalStatsRaster = os.path.join(outputGDB, "costFocalStatsRaster")
        outFocalStats.save(costFocalStatsRaster)
        arcpy.env.extent = resRast

        circleArea = float(npy.pi * radius * radius)
        outras = Raster(costFocalStatsRaster) * circleArea
        purchCostRaster = os.path.join(outputGDB, "purchaseCostRaster")
        outras.save(purchCostRaster)
        lu.delete_data(costFocalStatsRaster)

        # restCost = npy.pi * radius * radius * restCostPer_m2
        restorationCostRaster = os.path.join(restorationDataGDB, restorationCostRaster)
        outras = Raster(purchCostRaster) + (Raster(restorationCostRaster) * radius * radius * npy.pi)
        totalCostRaster = os.path.join(outputGDB, "totalCostRaster")
        outras.save(totalCostRaster)
        # lu.build_stats(totalCostRaster)

        # Create mask to remove areas without cost data
        arcpy.env.extent = totalCostRaster
        costMaskRaster = os.path.join(outputGDB, "costMaskRaster")
        costThresh = 0
        outCon = arcpy.sa.Con((Raster(totalCostRaster) > float(costThresh)), 1)
        outCon.save(costMaskRaster)
        arcpy.env.extent = resRast

        # Create mask to remove areas below ag threshold
        outCon = arcpy.sa.Con((Raster(proportionAgRaster) > float(minAgThreshold)), 1)
        agMaskRaster = os.path.join(outputGDB, "agMaskRaster")
        outCon.save(agMaskRaster)

        doStep1 = "true"
        doStep2 = "true"
        doStep5 = "false"
        for iter in range(1, iterations + 1):  # xxx
            start_time1 = time.clock()
            arcpy.env.cellSize = resRast  # Some env settings get changed by linkage mapper and must be reset here
            arcpy.env.extent = resRast
            arcpy.env.snapRaster = resRast
            arcpy.env.overwriteOutput = True
            arcpy.env.scratchWorkspace = outputGDB
            arcpy.env.workspace = outputGDB

            lu.dashline(1)
            gprint("Running iteration number " + str(iter))
            projDir = os.path.join(outputDir, "iter" + str(iter) + "Proj")
            lu.create_dir(outputDir)
            lu.delete_dir(projDir)  # xxx
            lu.create_dir(projDir)
            if iter > 1:  # Copy previous s2 linktable to new project directory
                datapassDir = os.path.join(projDir, "datapass")
                lu.create_dir(datapassDir)
                projDir1 = os.path.join(outputDir, "iter1Proj")
                datapassDirIter1 = os.path.join(projDir1, "datapass")
                s2LinktableIter1 = os.path.join(datapassDirIter1, "linkTable_s2.csv")
                s2LinkTable = os.path.join(datapassDir, "linkTable_s2.csv")
                shutil.copyfile(s2LinktableIter1, s2LinkTable)

            # Run Linkage Mapper
            distFile = os.path.join(
                outputDir, coreFC + "_dists.txt"
            )  # Copy distances text file from earlier LM run to the output directory- speeds things up!
            if not os.path.exists(distFile):
                if iter == 1:
                    gprint("Will calculate distance file.")
                    distFile = "#"
                else:
                    projDir1 = os.path.join(outputDir, "iter1Proj")
                    distFile1 = os.path.join(projDir1, coreFC + "_dists.txt")
                    shutil.copyfile(distFile1, distFile)  # Put a copy here for future runs

            arcpy.env.overwriteOutput = True
            arcpy.env.scratchWorkspace = outputGDB
            arcpy.env.workspace = outputGDB

            argv = (
                "lm_master.py",
                projDir,
                coreFCPath,
                coreFN,
                resRast,
                doStep1,
                doStep2,
                "Cost-Weighted & Euclidean",
                distFile,
                "true",
                "true",
                "false",
                "4",
                "Cost-Weighted",
                "true",
                doStep5,
                "10000",
                "#",
                "#",
            )
            gprint("Running " + str(argv))
            import lm_master  # xxx

            lm_master.lm_master(argv)  # xxx
            doStep1 = "false"  # Can skip for future iterations
            doStep2 = "false"  # Can skip for future iterations
            doStep5 = "false"  # Skipping for future iterations

            startRadius = str(radius)
            endRadius = str(radius)
            radiusStep = "0"
            saveRadiusRasters = "false"
            writePctRasters = "false"

            argv = (
                "barrier_master.py",
                projDir,
                resRast,
                startRadius,
                endRadius,
                radiusStep,
                barrierCombineMethod,
                saveRadiusRasters,
                writePctRasters,
                cwdThresh,
            )
            gprint("Running " + str(argv))
            import barrier_master  # xxx

            barrier_master.bar_master(argv)  # xxx

            arcpy.env.cellSize = resRast  # Some env settings get changed by linkage mapper and must be reset here
            arcpy.env.extent = resRast
            arcpy.env.snapRaster = resRast
            arcpy.env.overwriteOutput = True
            arcpy.env.scratchWorkspace = outputGDB
            arcpy.env.workspace = outputGDB

            gprint("Finding restoration circles with max barrier score / ROI")
            # Find points with max ROI
            PREFIX = os.path.basename(projDir)
            if barrierCombineMethod == "Sum":
                sumSuffix = "Sum"
            else:
                sumSuffix = ""
            barrierFN = PREFIX + "_BarrierCenters" + sumSuffix + "_Rad" + str(radius)
            barrierRaster = os.path.join(projDir, "output", "barriers.gdb", barrierFN)
            if not arcpy.Exists(barrierRaster):
                msg = "Error: cannot find barrier output: " + barrierRaster
                lu.raise_error(msg)

            # arcpy.env.cellSize = agMaskRaster
            # arcpy.env.extent = agMaskRaster

            if iter > 1:
                gprint("Creating mask for previously restored areas")
                inNeighborhood = NbrCircle(radius, "MAP")
                arcpy.env.extent = allRestoredAreasRaster
                outFocalStats = arcpy.sa.FocalStatistics(allRestoredAreasRaster, inNeighborhood, "MEAN", "DATA")
                allRestoredFocalRaster = os.path.join(outputGDB, "allRestFocRas_iter" + str(iter))
                outFocalStats.save(allRestoredFocalRaster)  # Anything > 0 would include a restored area and
                arcpy.env.extent = resRast
                restMaskRaster = os.path.join(outputGDB, "restMaskRaster_iter" + str(iter))
                minval = 0
                outCon = arcpy.sa.Con((Raster(allRestoredFocalRaster) == float(minval)), 1)
                outCon.save(restMaskRaster)

            # Candidate areas have not been restored, have cost data, meet
            # minimum improvement score criteria, and have enough ag in them
            candidateBarrierRaster = os.path.join(outputGDB, "candidateBarrierRaster" + "_iter" + str(iter))
            if iter > 1:
                gprint(
                    "Creating candidate restoration raster using barrier results, previous restorations, and selection criteria"
                )
                outCalc = (
                    Raster(costMaskRaster)
                    * Raster(agMaskRaster)
                    * Raster(barrierRaster)
                    * Raster(restMaskRaster)
                    * (radius * 2)
                )  # ROI scores will be in terms of total improvement (= score * diameter)
            else:
                outCalc = Raster(costMaskRaster) * Raster(agMaskRaster) * Raster(barrierRaster) * radius * 2

            minBarrierScore = minImprovementVal * radius * 2
            if restoredResistanceVal != 1:
                outCalc2 = outCalc - (2 * radius * (restoredResistanceVal - 1))
                outCon = arcpy.sa.Con((outCalc2 >= float(minBarrierScore)), outCalc2)
            else:
                outCon = arcpy.sa.Con((outCalc >= float(minBarrierScore)), outCalc)
            outCon.save(candidateBarrierRaster)
            lu.build_stats(candidateBarrierRaster)

            purchaseRoiRaster = os.path.join(outputGDB, "purchaseRoiRaster" + "_iter" + str(iter))
            outCalc = Raster(candidateBarrierRaster) / Raster(purchCostRaster)
            outCalc.save(purchaseRoiRaster)
            lu.build_stats(purchaseRoiRaster)

            totalRoiRaster = os.path.join(outputGDB, "purchaseRestRoiRaster" + "_iter" + str(iter))
            outCalc = Raster(candidateBarrierRaster) / Raster(totalCostRaster)
            outCalc.save(totalRoiRaster)
            lu.build_stats(totalRoiRaster)

            maxBarrier = arcpy.GetRasterProperties_management(candidateBarrierRaster, "MAXIMUM")
            gprint("Maximum barrier improvement score: " + str(maxBarrier.getOutput(0)))
            if maxBarrier < 0:
                arcpy.AddWarning("\nNo barriers found that meet CWD or Ag threshold criteria.")

            maxPurchROI = arcpy.GetRasterProperties_management(purchaseRoiRaster, "MAXIMUM")
            gprint("Maximum purchase ROI score: " + str(maxPurchROI.getOutput(0)))

            maxROI = arcpy.GetRasterProperties_management(totalRoiRaster, "MAXIMUM")
            gprint("Maximum total ROI score: " + str(maxROI.getOutput(0)))

            if restoreMaxROI:
                outPoint = os.path.join(outputGDB, "maxRoiPoint" + "_iter" + str(iter))
                gprint("Choosing circle with maximum ROI to restore")
                outCon = arcpy.sa.Con((Raster(totalRoiRaster) >= float(maxROI.getOutput(0))), totalRoiRaster)
                maxRoiRaster = os.path.join(outputGDB, "maxRoiRaster")
                outCon.save(maxRoiRaster)
                # Save max ROI to point
                try:
                    arcpy.RasterToPoint_conversion(maxRoiRaster, outPoint)
                except:
                    msg = "Error: it looks like there are no viable restoration candidates."
                    lu.raise_error(msg)

            else:  # Restoring strongest barrier instead
                outPoint = os.path.join(outputGDB, "maxBarrierPoint" + "_iter" + str(iter))
                gprint("Choosing circle with maximum BARRIER IMPROVEMENT SCORE to restore")
                outCon = arcpy.sa.Con(
                    (Raster(candidateBarrierRaster) >= float(maxBarrier.getOutput(0))), candidateBarrierRaster
                )
                maxBarrierRaster = os.path.join(outputGDB, "maxBarrierRaster")
                outCon.save(maxBarrierRaster)
                # Save max barrier to point
                try:
                    arcpy.RasterToPoint_conversion(maxBarrierRaster, outPoint)
                except:
                    msg = "Error: it looks like there are no viable restoration candidates."
                    lu.raise_error(msg)

            gprint("Done evaluating candidate restorations")
            result = int(arcpy.GetCount_management(outPoint).getOutput(0))
            if result > 1:
                arcpy.AddWarning(
                    "Deleting points with identical ROI/improvement score values"
                )  # Would be better to retain point with max barrier score when we have multiple points with same ROI
                arcpy.DeleteIdentical_management(outPoint, "grid_code", 0.1, 0.1)
            arcpy.sa.ExtractMultiValuesToPoints(
                outPoint,
                [
                    [candidateBarrierRaster, "barrierScore"],
                    [purchCostRaster, "purchCost"],
                    [totalCostRaster, "totalCost"],
                    [purchaseRoiRaster, "purchaseROI"],
                    [totalRoiRaster, "totalROI"],
                ],
                "NONE",
            )
            arcpy.AddField_management(outPoint, "restorationNumber", "SHORT")
            arcpy.CalculateField_management(outPoint, "restorationNumber", iter)
            arcpy.AddField_management(outPoint, "radius", "DOUBLE")
            arcpy.CalculateField_management(outPoint, "radius", radius)
            arcpy.AddField_management(outPoint, "barrierScore_per_m", "DOUBLE")
            arcpy.CalculateField_management(
                outPoint, "barrierScore_per_m", "(float(!barrierScore!) / (!radius! * 2))", "PYTHON"
            )

            gprint("\nCreating restoration circles")
            if restoreMaxROI:
                circleFC = os.path.join(outputGDB, "maxRoiCircle" + "_iter" + str(iter))
            else:
                circleFC = os.path.join(outputGDB, "maxBarrierCircle" + "_iter" + str(iter))
            arcpy.Buffer_analysis(outPoint, circleFC, radius)
            gprint("Rasterizing restoration circles")
            if restoreMaxROI:
                circleRas = os.path.join(outputGDB, "maxRoiCircleRas" + "_iter" + str(iter))
            else:
                circleRas = os.path.join(outputGDB, "maxBarrierCircleRas" + "_iter" + str(iter))
            arcpy.FeatureToRaster_conversion(circleFC, "totalROI", circleRas, arcpy.env.cellSize)

            # restore raster
            gprint("Digitally restoring resistance raster")
            resRastRestored = os.path.join(outputGDB, "resRastRestored" + "_iter" + str(iter))
            outCon = arcpy.sa.Con(IsNull(circleRas), resRast, restoredResistanceVal)
            outCon.save(resRastRestored)

            allRestoredAreasRaster = os.path.join(outputGDB, "allRestoredAreas_iter" + str(iter))
            PrevRestoredAreasRaster = os.path.join(outputGDB, "allRestoredAreas_iter" + str(iter - 1))
            if iter == 1:
                outCon = arcpy.sa.Con(IsNull(circleRas), 0, 1)
            else:
                outCon = arcpy.sa.Con(
                    IsNull(circleRas), PrevRestoredAreasRaster, 1
                )  # Add this restoration to areas restored
            outCon.save(allRestoredAreasRaster)

            lu.delete_data(circleRas)
            resRast = resRastRestored  # Use for next iteration resistance raster

            # Add circle into feature class with all circles
            if restoreMaxROI:
                allCirclesFC = os.path.join(outputGDB, "allCirclesMaxROI")
            else:
                allCirclesFC = os.path.join(outputGDB, "allCirclesMaxBarriers")
            if iter == 1:
                arcpy.CopyFeatures_management(circleFC, allCirclesFC)
            else:
                arcpy.Append_management(circleFC, allCirclesFC, "TEST")
            gprint("Finished iteration #" + str(iter))
            start_time1 = lu.elapsed_time(start_time1)

        gprint("\nDone with iterations.")
        start_time = lu.elapsed_time(start_time)
        gprint("Outputs saved in: " + outputGDB)
        gprint("Back up your project directories if you want to save corridor/barrier results.")

    # Return GEOPROCESSING specific errors
    except arcpy.ExecuteError:
        lu.dashline(1)
        gprint("****Iteration script failed. Details follow.****")
        lu.exit_with_geoproc_error(_SCRIPT_NAME)

    # Return any PYTHON or system specific errors
    except:
        lu.dashline(1)
        gprint("****Iteration script failed. Details follow.****")
        lu.exit_with_python_error(_SCRIPT_NAME)
def main():
    """Iterates over LM, BM, and restoration tasks"""

    ## USER SETTINGS ######################################################
    ## Restoration Settings
    ## ALL input data must be in the same projection
    start_time = time.clock()
    restoreMaxROI = False # Set to True to restore highest ROI
                         # Set to False to restore strongest barrier
    restoredResistanceVal = 1 # Resistance value of restored habitat.  Must be 1 or greater.
    restorationDataGDB = "C:\\barrierClassAnalysis\\RestorationINPUTS_July2013.gdb" # No spaces or special chars in paths or gdb names
    outputDir = "C:\\barrierClassAnalysis\\output" # No spaces in path, avoid using dropbox or network drive
                                                   # Project directories will be created in this (iter1, iter2...)
                                                   # as will an output geodatabase
    resistanceRaster = "URWA_resis"# Resistance raster.  Should be in input GDB
    coreFC = 'URWA_HCAs_Doug_Grant'# Core area feature class. Should be in input GDB 'URWA_HCAs_Doug_Grant'
    coreFN = 'HCA_ID' # Core area field name
    
    radius = 450 # restoration radius in meters
    iterations = 13 # number of restorations to perform
    minAgThreshold = 0.75 # if less than this proportion of ag in circle, don't consider restoring circle
    minImprovementVal = 0 # Don't consider barriers below this improvement score (average improvement per meter diameter restored)
    parcelCostRaster = 'DougGrantParcelCost_m2_projected_90m' # Average per-m2 parcel cost per pixel. Snapped to resistance raster.
    restorationCostRaster = 'restCostPer_m2' # Right now this is just a raster with all pixels set to 0.113174
    agRaster = "ARESmaskp_projected" # 1=Ag, 0 = not Ag
    barrierCombineMethod = 'Maximum' # Some restorations benefit multiple corridors. 
                                     # 'Maximum' takes the greatest improvement across core area pairs
                                     # 'Sum' adds improvement scores acreoss all pairs. 
    cwdThresh = None # Use cwdThresh = None for no threshold. Use cwdThresh = X to not consider 
                      # restorations more than X map units away from each core area.
    ## END USER SETTINGS ######################################################
    try:
        # Setup path and create directories
        gprint('Hey! Make sure everything is in the same projection!\n')
        gprint('Setting up paths and creating directories')
        sys.path.append('..\\toolbox\\scripts')
        resRast = os.path.join(restorationDataGDB, resistanceRaster)   
        coreFCPath = os.path.join(restorationDataGDB, coreFC)

        # Set up a NEW output gdb (leave previous ones on drive)
        for i in range (1,200):
            outputGDB = 'restorationOutput'+str(i)+'.gdb'
            if not arcpy.Exists(os.path.join(outputDir,outputGDB)):
                break
            gprint('Previous output GDB '+ outputGDB +' exists.  Delete to save disk space.')    
        arcpy.CreateFileGDB_management(outputDir,outputGDB)
        outputGDB = os.path.join(outputDir,outputGDB)
        logFile = os.path.join(outputGDB,'Iterate Barriers'+str(i)+'.py')
        shutil.copyfile(__file__, logFile) #write a copy of this file to output dir as a record of settings
        
        arcpy.env.cellSize = resRast
        arcpy.env.extent = resRast
        arcpy.env.snapRaster = resRast
        arcpy.env.overwriteOutput = True
        arcpy.env.scratchWorkspace = outputGDB
        arcpy.env.workspace = outputGDB
        
        spatialref = arcpy.Describe(resRast).spatialReference
        mapunits = spatialref.linearUnitName
        gprint('Cell size = ' + str(arcpy.env.cellSize) + ' ' + mapunits +'s')    
        
        
        # Calculate fraction of ag within radius of each pixel
        gprint('Calculating purchase cost, fraction of ag, etc within radius of each pixel.')
        agRaster = os.path.join(restorationDataGDB, agRaster)
        inNeighborhood = NbrCircle(radius, "MAP")
        arcpy.env.extent = agRaster
        outFocalStats = arcpy.sa.FocalStatistics(agRaster,
                                        inNeighborhood, "MEAN","NODATA")
        proportionAgRaster = os.path.join(outputGDB,'proportionAgRas')
        outFocalStats.save(proportionAgRaster)    
        arcpy.env.extent = resRast

        # Calculate purchase cost of circles
        parcelCostRaster = os.path.join(restorationDataGDB, parcelCostRaster)
        arcpy.env.extent = parcelCostRaster
        outFocalStats = arcpy.sa.FocalStatistics(parcelCostRaster,inNeighborhood, "MEAN","DATA")
        costFocalStatsRaster = os.path.join(outputGDB,'costFocalStatsRaster')
        outFocalStats.save(costFocalStatsRaster)
        arcpy.env.extent = resRast
        
        circleArea = float(npy.pi * radius * radius)
        outras = (Raster(costFocalStatsRaster) * circleArea)
        purchCostRaster = os.path.join(outputGDB,'purchaseCostRaster')
        outras.save(purchCostRaster)
        lu.delete_data(costFocalStatsRaster)
        
        # restCost = npy.pi * radius * radius * restCostPer_m2
        restorationCostRaster = os.path.join(restorationDataGDB, restorationCostRaster)
        outras = Raster(purchCostRaster) + (Raster(restorationCostRaster) * radius * radius * npy.pi)
        totalCostRaster = os.path.join(outputGDB,'totalCostRaster')
        outras.save(totalCostRaster)
        # lu.build_stats(totalCostRaster)
        
        # Create mask to remove areas without cost data
        arcpy.env.extent = totalCostRaster
        costMaskRaster = os.path.join(outputGDB,'costMaskRaster')
        costThresh = 0
        outCon = arcpy.sa.Con((Raster(totalCostRaster) > float(costThresh)), 1)
        outCon.save(costMaskRaster)
        arcpy.env.extent = resRast
        
        # Create mask to remove areas below ag threshold
        outCon = arcpy.sa.Con((Raster(proportionAgRaster) > float(minAgThreshold)), 1)
        agMaskRaster = os.path.join(outputGDB, 'agMaskRaster')
        outCon.save(agMaskRaster)       
        
        doStep1 = 'true'
        doStep2 = 'true'
        doStep5 = 'false'
        for iter in range(1,iterations+1): #xxx
            start_time1 = time.clock()
            arcpy.env.cellSize = resRast # Some env settings get changed by linkage mapper and must be reset here
            arcpy.env.extent = resRast
            arcpy.env.snapRaster = resRast
            arcpy.env.overwriteOutput = True
            arcpy.env.scratchWorkspace = outputGDB
            arcpy.env.workspace = outputGDB

            lu.dashline(1)
            gprint('Running iteration number '+str(iter))
            projDir = os.path.join(outputDir,'iter' + str(iter)+'Proj')    
            lu.create_dir(outputDir)
            lu.delete_dir(projDir) #xxx
            lu.create_dir(projDir)
            if iter > 1: # Copy previous s2 linktable to new project directory
                datapassDir = os.path.join(projDir,'datapass')
                lu.create_dir(datapassDir)
                projDir1 = os.path.join(outputDir,'iter1Proj')
                datapassDirIter1 = os.path.join(projDir1,'datapass')
                s2LinktableIter1 = os.path.join(datapassDirIter1 ,'linkTable_s2.csv')
                s2LinkTable = os.path.join(datapassDir ,'linkTable_s2.csv')
                shutil.copyfile(s2LinktableIter1, s2LinkTable)

            
            # Run Linkage Mapper
            distFile = os.path.join(outputDir, coreFC + '_dists.txt') # Copy distances text file from earlier LM run to the output directory- speeds things up!
            if not os.path.exists(distFile):
                if iter == 1:
                    gprint('Will calculate distance file.')
                    distFile = '#'
                else:
                    projDir1 = os.path.join(outputDir,'iter1Proj')
                    distFile1 = os.path.join(projDir1, coreFC + '_dists.txt')
                    shutil.copyfile(distFile1,distFile) # Put a copy here for future runs
                    
            arcpy.env.overwriteOutput = True
            arcpy.env.scratchWorkspace = outputGDB
            arcpy.env.workspace = outputGDB

            argv = ('lm_master.py', projDir, coreFCPath, coreFN, resRast,
                    doStep1, doStep2, 'Cost-Weighted & Euclidean', distFile,
                    'true', 'true', 'false', '4', 'Cost-Weighted', 'true',
                    doStep5, 'true', '200000', '10000', '#', '#', '#', '#') 
            gprint('Running ' + str(argv))
            cfg.lm_configured = False  # Insures lm_master uses current argv
            lm_master.lm_master(argv)    #xxx
            doStep1 = 'false' # Can skip for future iterations
            doStep2 = 'false' # Can skip for future iterations        
            doStep5 = 'false' # Skipping for future iterations
            
            startRadius = str(radius)
            endRadius = str(radius)
            radiusStep = '0'
            saveRadiusRasters= 'false'
            writePctRasters = 'false'

            argv = ('barrier_master.py', projDir, resRast, startRadius, endRadius, radiusStep, barrierCombineMethod,
                    saveRadiusRasters, writePctRasters, cwdThresh)
            gprint('Running ' + str(argv))
            barrier_master.bar_master(argv) #xxx

            arcpy.env.cellSize = resRast # Some env settings get changed by linkage mapper and must be reset here
            arcpy.env.extent = resRast
            arcpy.env.snapRaster = resRast
            arcpy.env.overwriteOutput = True
            arcpy.env.scratchWorkspace = outputGDB
            arcpy.env.workspace = outputGDB
            
            gprint('Finding restoration circles with max barrier score / ROI')
            # Find points with max ROI
            PREFIX = os.path.basename(projDir)
            if barrierCombineMethod == 'Sum':
                sumSuffix = 'Sum'
            else:
                sumSuffix = ''
            barrierFN = (PREFIX + "_BarrierCenters" + sumSuffix + "_Rad" + str(radius))
            barrierRaster = os.path.join(projDir,'output','barriers.gdb',barrierFN)
            if not arcpy.Exists(barrierRaster):
                msg = ('Error: cannot find barrier output: '+barrierRaster)
                lu.raise_error(msg)

            # arcpy.env.cellSize = agMaskRaster
            # arcpy.env.extent = agMaskRaster

            if iter > 1:
                gprint('Creating mask for previously restored areas')
                inNeighborhood = NbrCircle(radius, "MAP")
                arcpy.env.extent = allRestoredAreasRaster
                outFocalStats = arcpy.sa.FocalStatistics(allRestoredAreasRaster,inNeighborhood, "MEAN","DATA")
                allRestoredFocalRaster = os.path.join(outputGDB,'allRestFocRas_iter'+str(iter))
                outFocalStats.save(allRestoredFocalRaster) # Anything > 0 would include a restored area and 
                arcpy.env.extent = resRast
                restMaskRaster = os.path.join(outputGDB,'restMaskRaster_iter'+str(iter))
                minval = 0
                outCon = arcpy.sa.Con((Raster(allRestoredFocalRaster) == float(minval)), 1)
                outCon.save(restMaskRaster)
                
            # Candidate areas have not been restored, have cost data, meet
            # minimum improvement score criteria, and have enough ag in them
            candidateBarrierRaster = os.path.join(outputGDB, 'candidateBarrierRaster' + '_iter'+str(iter))
            if iter > 1:
                gprint('Creating candidate restoration raster using barrier results, previous restorations, and selection criteria')
                outCalc = (Raster(costMaskRaster) * Raster(agMaskRaster) * Raster(barrierRaster) * Raster(restMaskRaster) * (radius * 2)) # ROI scores will be in terms of total improvement (= score * diameter)
            else:
                outCalc = (Raster(costMaskRaster) * Raster(agMaskRaster) * Raster(barrierRaster) * radius * 2)
            
            minBarrierScore = minImprovementVal * radius * 2
            if restoredResistanceVal != 1:
                outCalc2 = (outCalc - (2 * radius * (restoredResistanceVal - 1)))
                outCon = arcpy.sa.Con((outCalc2 >= float(minBarrierScore)), outCalc2)
            else:
                outCon = arcpy.sa.Con((outCalc >= float(minBarrierScore)), outCalc)
            outCon.save(candidateBarrierRaster)
            lu.build_stats(candidateBarrierRaster)
            
            purchaseRoiRaster = os.path.join(outputGDB, 'purchaseRoiRaster' + '_iter'+str(iter))
            outCalc = Raster(candidateBarrierRaster) / Raster(purchCostRaster) 
            outCalc.save(purchaseRoiRaster)
            lu.build_stats(purchaseRoiRaster)
            
            totalRoiRaster = os.path.join(outputGDB, 'purchaseRestRoiRaster' + '_iter'+str(iter))
            outCalc = Raster(candidateBarrierRaster) / Raster(totalCostRaster)
            outCalc.save(totalRoiRaster)
            lu.build_stats(totalRoiRaster)

            maxBarrier = arcpy.GetRasterProperties_management(candidateBarrierRaster,"MAXIMUM")
            gprint('Maximum barrier improvement score: '+str(maxBarrier.getOutput(0)))
            if maxBarrier < 0:
                arcpy.AddWarning("\nNo barriers found that meet CWD or Ag threshold criteria.")
            
            maxPurchROI = arcpy.GetRasterProperties_management(purchaseRoiRaster,"MAXIMUM")
            gprint('Maximum purchase ROI score: '+str(maxPurchROI.getOutput(0)))

            maxROI = arcpy.GetRasterProperties_management(totalRoiRaster,"MAXIMUM")
            gprint('Maximum total ROI score: '+str(maxROI.getOutput(0)))

            if restoreMaxROI:
                outPoint = os.path.join(outputGDB, 'maxRoiPoint'+'_iter'+str(iter))
                gprint('Choosing circle with maximum ROI to restore')
                outCon = arcpy.sa.Con((Raster(totalRoiRaster) >= float(maxROI.getOutput(0))), totalRoiRaster)
                maxRoiRaster = os.path.join(outputGDB, 'maxRoiRaster')
                outCon.save(maxRoiRaster)    
                # Save max ROI to point
                try:
                    arcpy.RasterToPoint_conversion(maxRoiRaster, outPoint)
                except:
                    msg = ('Error: it looks like there are no viable restoration candidates.')
                    lu.raise_error(msg)
        
            else: #Restoring strongest barrier instead
                outPoint = os.path.join(outputGDB, 'maxBarrierPoint'+'_iter'+str(iter))
                gprint('Choosing circle with maximum BARRIER IMPROVEMENT SCORE to restore')
                outCon = arcpy.sa.Con((Raster(candidateBarrierRaster) >= float(maxBarrier.getOutput(0))), candidateBarrierRaster)
                maxBarrierRaster = os.path.join(outputGDB, 'maxBarrierRaster')
                outCon.save(maxBarrierRaster)            
                # Save max barrier to point
                try:
                    arcpy.RasterToPoint_conversion(maxBarrierRaster, outPoint)
                except:
                    msg = ('Error: it looks like there are no viable restoration candidates.')
                    lu.raise_error(msg)            
            
            gprint('Done evaluating candidate restorations')
            result = int(arcpy.GetCount_management(outPoint).getOutput(0)) 
            if result > 1:
                arcpy.AddWarning('Deleting points with identical ROI/improvement score values') # Would be better to retain point with max barrier score when we have multiple points with same ROI
                arcpy.DeleteIdentical_management(outPoint, "grid_code", 0.1, 0.1)            
            arcpy.sa.ExtractMultiValuesToPoints(outPoint, 
                [[candidateBarrierRaster, "barrierScore"],[purchCostRaster, "purchCost"],
                [totalCostRaster, "totalCost"],[purchaseRoiRaster, "purchaseROI"],
                [totalRoiRaster, "totalROI"]], "NONE")
            arcpy.AddField_management(outPoint, "restorationNumber", "SHORT")
            arcpy.CalculateField_management(outPoint, "restorationNumber", iter)        
            arcpy.AddField_management(outPoint, "radius", "DOUBLE")
            arcpy.CalculateField_management(outPoint, "radius", radius)        
            arcpy.AddField_management(outPoint, "barrierScore_per_m", "DOUBLE")
            arcpy.CalculateField_management(outPoint, "barrierScore_per_m", "(float(!barrierScore!) / (!radius! * 2))", "PYTHON")        

            gprint('\nCreating restoration circles')
            if restoreMaxROI:
                circleFC = os.path.join(outputGDB, 'maxRoiCircle'+'_iter'+str(iter))
            else:
                circleFC = os.path.join(outputGDB, 'maxBarrierCircle'+'_iter'+str(iter))
            arcpy.Buffer_analysis(outPoint, circleFC, radius)
            gprint('Rasterizing restoration circles')
            if restoreMaxROI:
                circleRas = os.path.join(outputGDB, 'maxRoiCircleRas'+'_iter'+str(iter))
            else:
                circleRas = os.path.join(outputGDB, 'maxBarrierCircleRas'+'_iter'+str(iter))
            arcpy.FeatureToRaster_conversion(circleFC, 'totalROI', circleRas, arcpy.env.cellSize)    

            # restore raster
            gprint('Digitally restoring resistance raster')
            resRastRestored = os.path.join(outputGDB, 'resRastRestored'+'_iter'+str(iter))
            outCon = arcpy.sa.Con(IsNull(circleRas), resRast, restoredResistanceVal)
            outCon.save(resRastRestored)

            allRestoredAreasRaster = os.path.join(outputGDB, 'allRestoredAreas_iter'+str(iter))
            PrevRestoredAreasRaster= os.path.join(outputGDB, 'allRestoredAreas_iter'+str(iter-1))
            if iter == 1:
                outCon = arcpy.sa.Con(IsNull(circleRas), 0, 1)
            else:
                outCon = arcpy.sa.Con(IsNull(circleRas), PrevRestoredAreasRaster, 1) # Add this restoration to areas restored
            outCon.save(allRestoredAreasRaster)
            
            lu.delete_data(circleRas)
            resRast = resRastRestored # Use for next iteration resistance raster
            
            #Add circle into feature class with all circles
            if restoreMaxROI:
                allCirclesFC = os.path.join(outputGDB,"allCirclesMaxROI")
            else:
                allCirclesFC = os.path.join(outputGDB,"allCirclesMaxBarriers")
            if iter == 1:
                arcpy.CopyFeatures_management(circleFC, allCirclesFC)
            else: 
                arcpy.Append_management(circleFC, allCirclesFC, "TEST")           
            gprint('Finished iteration #'+str(iter))
            start_time1 = lu.elapsed_time(start_time1)    

        gprint('\nDone with iterations.')
        start_time = lu.elapsed_time(start_time)    
        gprint('Outputs saved in: '+outputGDB)
        gprint('Back up your project directories if you want to save corridor/barrier results.')

    # Return GEOPROCESSING specific errors
    except arcpy.ExecuteError:
        lu.dashline(1)
        gprint('****Iteration script failed. Details follow.****')
        lu.exit_with_geoproc_error(_SCRIPT_NAME)

    # Return any PYTHON or system specific errors
    except:
        lu.dashline(1)
        gprint('****Iteration script failed. Details follow.****')
        lu.exit_with_python_error(_SCRIPT_NAME)