Beispiel #1
0
def burn(subregion_ned, nhd_gdb, burnt_out, projection = arcpy.SpatialReference(102039)):
    env.snapRaster = subregion_ned
    env.outputCoordinateSystem = projection
    env.compression = "LZ77" # compress temp tifs for speed
    env.workspace = nhd_gdb

    # Copy flowlines to shapefile that will inherit environ output coord system
    flow_line = "NHDFlowline_Projected"
    if not arcpy.Exists(flow_line):
        arcpy.FeatureClassToFeatureClass_conversion("NHDFlowline", nhd_gdb, flow_line)

    cu.multi_msg("Prepared NHDFlowline for rasterizing.")

    # Feature to Raster- rasterize the NHDFlowline
    flow_line_raster = "in_memory/flow_line_raster"
    arcpy.FeatureToRaster_conversion(flow_line, "OBJECTID", flow_line_raster, "10")
    cu.multi_msg("Converted flowlines to raster.")

    # Raster Calculator- burns in streams, beveling in from 500m
    cu.multi_msg("Burning streams into raster, 10m deep and beveling in from 500m out. This may take a while....")
    distance = EucDistance(flow_line, cell_size = "10")
    streams = Reclassify(Raster(flow_line_raster) > 0, "Value", "1 1; NoData 0")
    burnt = Raster(subregion_ned) - (10 * streams) - (0.02 * (500 - distance) * (distance < 500))

    cu.multi_msg("Saving output raster...")
    burnt.save(burnt_out)

    # Delete intermediate rasters and shapefiles
    cu.cleanup([flow_line, flow_line_raster])
    cu.multi_msg("Burn process completed")
Beispiel #2
0
def unzip_ned(file_id, ned_dir, out_dir):
    # clunky but this works in USA: zipped files sometimes called
    # something like n36w87 instead of n36w087 so try all 3
    filename_variants = [
        os.path.join(ned_dir, f) for f in [
            file_id + ".zip", file_id[0:4] + file_id[5:] + ".zip", file_id[0] +
            file_id[2:] + ".zip"
        ]
    ]
    filename_to_use = ''
    for f in filename_variants:
        if not os.path.exists(f):
            continue
        else:
            filename_to_use = f

    if filename_to_use:
        cu.multi_msg("Unzipping file %s" % filename_to_use)
        zf = zipfile.ZipFile(filename_to_use)
        zf.extractall(out_dir)
        return True
    else:
        cu.multi_msg(
            "ERROR: A tile for %s does not exist in the specified location" %
            file_id)
        return False
def stats_overlap(non_overlapping_zones_list, zone_field, in_value_raster, out_table, is_thematic):
    temp_out_tables = ['in_memory/' + os.path.basename(zfc) + "_temp_table" for zfc in non_overlapping_zones_list]

    arcpy.CheckOutExtension("Spatial")
    for zones, temp_table in zip(non_overlapping_zones_list, temp_out_tables):
        cu.multi_msg('Calculating statistics for layer {0}'.format(zones))
        zonal_tabarea.stats_area_table(zones, zone_field, in_value_raster, temp_table, is_thematic)
    arcpy.CheckInExtension("Spatial")

    # doing this append/copy method instead of merge prevents problems with
    # differences in the field length of the zone field created by
    # Zonal Statistics As Table, merge doesn't have 'NO_TEST' option.
    target_table = temp_out_tables.pop(0)
    arcpy.Append_management(temp_out_tables, target_table, 'NO_TEST')
    arcpy.CopyRows_management(target_table, out_table)

    in_count = 0
    for zones in non_overlapping_zones_list:
        in_count += int(arcpy.GetCount_management(zones).getOutput(0))
    out_count = int(arcpy.GetCount_management(out_table).getOutput(0))
    if out_count < in_count:
        warn_msg = ("WARNING: {0} features are missing in the output table"
                    " because they are too small for this raster's"
                    " resolution. This may be okay depending on your"
                    " application.").format(in_count - out_count)
        arcpy.AddWarning(warn_msg)
        print(warn_msg)
    for t in temp_out_tables + [target_table]:
        arcpy.Delete_management(t)
def streams_in_zones(zones_fc, zone_field, streams_fc, output_table):

    arcpy.env.workspace = 'in_memory'

    # this bit enforces the correct ftype restriction just in case
    # geodata doesn't have this filtered already
    # this won't be as good as doing it before hand because it's
    # not sophisticated enough to take out the artificial lines going
    # through lakes
    need_selection = False
    with arcpy.da.SearchCursor(streams_fc, ["FType"]) as cursor:
            for row in cursor:
                if row[0] == 566:
                    need_selection = True

    if need_selection:
        whereClause = """"FType" <> 566"""
        arcpy.Select_analysis(temp_lakes, "no_coastlines", whereClause)
        streams_fc = os.path.join(arcpy.env.workspace, "no_coastlines")


    selections = ['',
                """"Strahler" <= 3""",
                """"Strahler" > 3 AND "Strahler" <= 6""",
                """"Strahler" > 6""",
                ]
    temp_tables = ['Streams', 'Headwaters', 'Midreaches', 'Rivers']

    for sel, temp_table in zip(selections, temp_tables):
        cu.multi_msg("Creating temporary table called {0} for streams where {1}".format(temp_table, sel))
        LineDensity.line_density(zones_fc, zone_field, streams_fc, temp_table, sel)
        new_fields = ['SUM_LengthM', 'Density_MperHA']
        for f in new_fields:
            cu.rename_field(temp_table, f, temp_table + '_' + f, True)

    # join em up and copy to final
    temp_tables.remove('Streams')
    for t in temp_tables:
        try:
            arcpy.JoinField_management('Streams', zone_field, t, zone_field)
        #sometimes there's no table if it was an empty selection
        except:
            empty_fields = [temp_table + '_' + f for f in new_fields]
            for ef in empty_fields:
                arcpy.AddField_management('Streams', ef, 'Double')
                arcpy.CalculateField_management('Streams', ef, '0', 'PYTHON')
            continue

    # remove all the extra zoneID fields, which have underscore in name
    drop_fields = [f.name for f in arcpy.ListFields('Streams', zone_field + '_*')]
    for f in drop_fields:
        arcpy.DeleteField_management('Streams', f)
    arcpy.CopyRows_management('Streams', output_table)

    # clean up
    for item in ['Streams', 'no_coastlines'] + temp_tables:
        try:
            arcpy.Delete_management(item)
        except:
            continue
Beispiel #5
0
def clip_to_hu8(raster,
                nhd_gdb,
                out_dir,
                projection=arcpy.SpatialReference(102039)):
    """Outputs a series of rasters, each one clipped to a different HU8. """
    env.workspace = 'in_memory'
    env.outputCoordinateSystem = projection
    env.compression = "NONE"  # only final tifs are generated
    env.snapRaster = raster
    env.cellSize = '10'
    env.pyramids = "PYRAMIDS -1 SKIP_FIRST"
    arcpy.CheckOutExtension("Spatial")

    # HUC8 polygons each saved as separate fc inheriting albers from environ
    huc8_fc = os.path.join(nhd_gdb, "WBD_HU8")
    arcpy.MakeFeatureLayer_management(huc8_fc, "huc8_layer")
    huc4_code = re.search('\d{4}', os.path.basename(nhd_gdb)).group()

    clips_dir = os.path.join(out_dir, 'huc8clips{0}'.format(huc4_code))
    if not os.path.exists(clips_dir):
        os.mkdir(clips_dir)


##    # add walls
##    arcpy.PolygonToLine_management(huc8_fc, 'wall_lines')
##    arcpy.AddField_management('wall_lines', "height", "DOUBLE")
##    arcpy.CalculateField_management('wall_lines', "height", '500', "PYTHON")
##    arcpy.FeatureToRaster_conversion('wall_lines', "height", 'wall_raster')
##    wallsObject = Raster('wall_raster')
##    elevObject = Raster(raster)
##    walled_ned = Con(IsNull(wallsObject), elevObject,
##                    (wallsObject + elevObject))

# for each HU8 feature in the fc, make a clip
    with arcpy.da.SearchCursor(huc8_fc, ["HUC_8"]) as cursor:
        for row in cursor:
            if row[0].startswith(huc4_code):
                whereClause = """"{0}" = '{1}'""".format("HUC_8", row[0])
                arcpy.SelectLayerByAttribute_management(
                    "huc8_layer", 'NEW_SELECTION', whereClause)
                arcpy.CopyFeatures_management("huc8_layer", "this_hu8")

                # clip the raster
                out_raster = os.path.join(clips_dir,
                                          'NED{0}.tif'.format(row[0]))
                cu.multi_msg('Creating output {0}'.format(out_raster))

                # use a small buffer here because otherwise the walls get
                # cut off in slivers
                arcpy.Buffer_analysis('this_hu8', 'this_hu8_buffer', 5000)
                arcpy.Clip_management(raster, '', out_raster,
                                      'this_hu8_buffer', '#',
                                      'ClippingGeometry')
                arcpy.Delete_management('this_hu8')
                arcpy.Delete_management('this_hu8_buffer')

    arcpy.Delete_management('huc8_layer')
    arcpy.ResetEnvironments()
    arcpy.CheckInExtension("Spatial")
def clip_to_hu8(raster, nhd_gdb, out_dir,
                    projection = arcpy.SpatialReference(102039)):
    """Outputs a series of rasters, each one clipped to a different HU8. """
    env.workspace = 'in_memory'
    env.outputCoordinateSystem = projection
    env.compression = "NONE" # only final tifs are generated
    env.snapRaster = raster
    env.cellSize = '10'
    env.pyramids = "PYRAMIDS -1 SKIP_FIRST"
    arcpy.CheckOutExtension("Spatial")

    # HUC8 polygons each saved as separate fc inheriting albers from environ
    huc8_fc = os.path.join(nhd_gdb, "WBD_HU8")
    arcpy.MakeFeatureLayer_management(huc8_fc, "huc8_layer")
    huc4_code = re.search('\d{4}', os.path.basename(nhd_gdb)).group()

    clips_dir = os.path.join(out_dir, 'huc8clips{0}'.format(huc4_code))
    if not os.path.exists(clips_dir):
        os.mkdir(clips_dir)

##    # add walls
##    arcpy.PolygonToLine_management(huc8_fc, 'wall_lines')
##    arcpy.AddField_management('wall_lines', "height", "DOUBLE")
##    arcpy.CalculateField_management('wall_lines', "height", '500', "PYTHON")
##    arcpy.FeatureToRaster_conversion('wall_lines', "height", 'wall_raster')
##    wallsObject = Raster('wall_raster')
##    elevObject = Raster(raster)
##    walled_ned = Con(IsNull(wallsObject), elevObject,
##                    (wallsObject + elevObject))

    # for each HU8 feature in the fc, make a clip
    with arcpy.da.SearchCursor(huc8_fc, ["HUC_8"]) as cursor:
        for row in cursor:
            if row[0].startswith(huc4_code):
                whereClause = """"{0}" = '{1}'""".format("HUC_8", row[0])
                arcpy.SelectLayerByAttribute_management("huc8_layer", 'NEW_SELECTION', whereClause)
                arcpy.CopyFeatures_management("huc8_layer", "this_hu8")

                # clip the raster
                out_raster = os.path.join(clips_dir, 'NED{0}.tif'.format(row[0]))
                cu.multi_msg('Creating output {0}'.format(out_raster))

                # use a small buffer here because otherwise the walls get
                # cut off in slivers
                arcpy.Buffer_analysis('this_hu8', 'this_hu8_buffer', 5000)
                arcpy.Clip_management(raster, '', out_raster,
                                    'this_hu8_buffer', '#',
                                    'ClippingGeometry')
                arcpy.Delete_management('this_hu8')
                arcpy.Delete_management('this_hu8_buffer')

    arcpy.Delete_management('huc8_layer')
    arcpy.ResetEnvironments()
    arcpy.CheckInExtension("Spatial")
def wetlands_in_zones(infolder, idfield, wetlands, top_outfolder):


    # Create output geodatabase in outfolder
    out_gdb = os.path.join(top_outfolder, "WetlandsInZones.gdb")
    if not arcpy.Exists(out_gdb):
        arcpy.CreateFileGDB_management(top_outfolder, "WetlandsInZones")


    # Add WetlandHa field if it doesn't exist
    if len(arcpy.ListFields(wetlands, 'WetlandHa')) == 0:
        arcpy.AddField_management(wetlands, "WetlandHa", "DOUBLE")
    expha = "!shape.area@hectares!"
    arcpy.CalculateField_management(wetlands, "WetlandHa", expha, "PYTHON")

    # Set in memory as workspace. Intermediate output will be held in RAM.
    mem = "in_memory"
    arcpy.env.workspace = mem

    # Make wetlands in memory.
    exp = """"ATTRIBUTE" LIKE 'P%'AND "WETLAND_TY" <> 'Freshwater_Pond'"""
    arcpy.Select_analysis(wetlands, "wetlandspoly", exp)

    # Convert wetlands to points
    arcpy.FeatureToPoint_management("wetlandspoly", "wetlands", "INSIDE")

    # List extent feature classes
    fcs = []
    for root, dirs, files in arcpy.da.Walk(infolder):
        for file in files:
            fcs.append(os.path.join(root,file))

    # Spatial Join the wetlands to each extent
    out_fcs = []
    for fc in fcs:
        cu.multi_msg("Creating results for %s" % fc)
        name = os.path.basename(fc)
        fms = arcpy.FieldMappings()
        fmid = arcpy.FieldMap()
        fmha = arcpy.FieldMap()
        fmid.addInputField(fc, idfield)
        fmha.addInputField("wetlands", "WetlandHa")
        fmha.mergeRule = 'Sum'
        fms.addFieldMap(fmid)
        fms.addFieldMap(fmha)
        out_fc = os.path.join(out_gdb, name + "_Wetlands")
        arcpy.SpatialJoin_analysis(fc, wetlands, out_fc ,'','',fms)
        out_fcs.append(out_fc)


    # Export feature classes attribute tables to tables
    for f in out_fcs:
        arcpy.CopyRows_management(f, os.path.join(out_gdb, "Table" + os.path.basename(f)))
Beispiel #8
0
def wetlands_in_zones(infolder, idfield, wetlands, top_outfolder):

    # Create output geodatabase in outfolder
    out_gdb = os.path.join(top_outfolder, "WetlandsInZones.gdb")
    if not arcpy.Exists(out_gdb):
        arcpy.CreateFileGDB_management(top_outfolder, "WetlandsInZones")

    # Add WetlandHa field if it doesn't exist
    if len(arcpy.ListFields(wetlands, 'WetlandHa')) == 0:
        arcpy.AddField_management(wetlands, "WetlandHa", "DOUBLE")
    expha = "!shape.area@hectares!"
    arcpy.CalculateField_management(wetlands, "WetlandHa", expha, "PYTHON")

    # Set in memory as workspace. Intermediate output will be held in RAM.
    mem = "in_memory"
    arcpy.env.workspace = mem

    # Make wetlands in memory.
    exp = """"ATTRIBUTE" LIKE 'P%'AND "WETLAND_TY" <> 'Freshwater_Pond'"""
    arcpy.Select_analysis(wetlands, "wetlandspoly", exp)

    # Convert wetlands to points
    arcpy.FeatureToPoint_management("wetlandspoly", "wetlands", "INSIDE")

    # List extent feature classes
    fcs = []
    for root, dirs, files in arcpy.da.Walk(infolder):
        for file in files:
            fcs.append(os.path.join(root, file))

    # Spatial Join the wetlands to each extent
    out_fcs = []
    for fc in fcs:
        cu.multi_msg("Creating results for %s" % fc)
        name = os.path.basename(fc)
        fms = arcpy.FieldMappings()
        fmid = arcpy.FieldMap()
        fmha = arcpy.FieldMap()
        fmid.addInputField(fc, idfield)
        fmha.addInputField("wetlands", "WetlandHa")
        fmha.mergeRule = 'Sum'
        fms.addFieldMap(fmid)
        fms.addFieldMap(fmha)
        out_fc = os.path.join(out_gdb, name + "_Wetlands")
        arcpy.SpatialJoin_analysis(fc, wetlands, out_fc, '', '', fms)
        out_fcs.append(out_fc)

    # Export feature classes attribute tables to tables
    for f in out_fcs:
        arcpy.CopyRows_management(
            f, os.path.join(out_gdb, "Table" + os.path.basename(f)))
def nhd_merge(gdb_list, example_feature_class_name, out_fc, selection = ''):
    arcpy.env.outputCoordinateSystem = arcpy.SpatialReference(102039) # USA_Contiguous_Albers_Equal_Area_Conic_USGS_version
    arcpy.env.workspace = 'in_memory'

    gdb_list = [os.path.join(gdb, os.path.basename(example_feature_class_name)) for gdb in gdb_list]
    gdb0 = gdb_list.pop(0)
    desc = arcpy.Describe(gdb0)
    cu.multi_msg('Merging all features together...')
    arcpy.CopyFeatures_management(gdb0, 'temp_merged')
    cu.lengthen_field('temp_merged', 'Permanent_Identifier', 255)
    cu.merge_many(gdb_list, 'in_memory/the_rest_merged')
    arcpy.Append_management('in_memory/the_rest_merged', 'temp_merged', 'NO_TEST')
    # use in_memory explicitly here because i haven't figured out how to pass arcgis environments to my functions :(
    fc_temp = 'in_memory/temp_merged'
    fcount1 = int(arcpy.GetCount_management(fc_temp).getOutput(0))
    cu.multi_msg('Before selection and cleaning, feature count is {0}'.format(fcount1))
    if selection:
        cu.multi_msg('Selecting features...')
        arcpy.Select_analysis('temp_merged', 'in_memory/merged_select', selection)
        fc_temp = 'in_memory/merged_select'

    fcount2 = int(arcpy.GetCount_management(fc_temp).getOutput(0))
    cu.multi_msg('After selection and before cleaning, feature count is {0}'.format(fcount2))

    arcpy.CopyFeatures_management(fc_temp, out_fc)
Beispiel #10
0
def main():
    subregion_ned = arcpy.GetParameterAsText(0)
    nhd_gdb = arcpy.GetParameterAsText(1)
    burnt_out = arcpy.GetParameterAsText(2)
    input_burnt = arcpy.GetParameter(3)

    arcpy.CheckOutExtension("Spatial")

    if not input_burnt:
        burn(subregion_ned, nhd_gdb, burnt_out, projection)
        input_burnt = burnt_out
    try:
        clip(input_burnt, nhd_gdb, projection, outfolder)
        arcpy.Delete_management(input_burnt)
        cu.multi_msg("Complete. HUC8 burned clips are now ready for flow direction.")
    except arcpy.ExecuteError:
        cu.multi_msg("Clip failed, try again. Mosaic file is %s and burnt NED file is %s" %
        (subregion_ned, burnt_ned))
        arcpy.AddError(arcpy.GetMessages(2))
    except Exception as e:
        cu.multi_msg("Clip failed, try again. Mosaic file is %s and burnt NED file is %s" %
        (subregion_ned, burnt_ned))
        cu.multi_msg(e.message)
    finally:
        arcpy.CheckInExtension("Spatial")
Beispiel #11
0
def main():
    subregion_ned = arcpy.GetParameterAsText(0)
    nhd_gdb = arcpy.GetParameterAsText(1)
    burnt_out = arcpy.GetParameterAsText(2)
    input_burnt = arcpy.GetParameter(3)

    arcpy.CheckOutExtension("Spatial")

    if not input_burnt:
        burn(subregion_ned, nhd_gdb, burnt_out, projection)
        input_burnt = burnt_out
    try:
        clip(input_burnt, nhd_gdb, projection, outfolder)
        arcpy.Delete_management(input_burnt)
        cu.multi_msg(
            "Complete. HUC8 burned clips are now ready for flow direction.")
    except arcpy.ExecuteError:
        cu.multi_msg(
            "Clip failed, try again. Mosaic file is %s and burnt NED file is %s"
            % (subregion_ned, burnt_ned))
        arcpy.AddError(arcpy.GetMessages(2))
    except Exception as e:
        cu.multi_msg(
            "Clip failed, try again. Mosaic file is %s and burnt NED file is %s"
            % (subregion_ned, burnt_ned))
        cu.multi_msg(e.message)
    finally:
        arcpy.CheckInExtension("Spatial")
Beispiel #12
0
def nhd_merge(gdb_list, example_feature_class_name, out_fc, selection=''):
    arcpy.env.outputCoordinateSystem = arcpy.SpatialReference(
        102039)  # USA_Contiguous_Albers_Equal_Area_Conic_USGS_version
    arcpy.env.workspace = 'in_memory'

    gdb_list = [
        os.path.join(gdb, os.path.basename(example_feature_class_name))
        for gdb in gdb_list
    ]
    gdb0 = gdb_list.pop(0)
    desc = arcpy.Describe(gdb0)
    cu.multi_msg('Merging all features together...')
    arcpy.CopyFeatures_management(gdb0, 'temp_merged')
    cu.lengthen_field('temp_merged', 'Permanent_Identifier', 255)
    cu.merge_many(gdb_list, 'in_memory/the_rest_merged')
    arcpy.Append_management('in_memory/the_rest_merged', 'temp_merged',
                            'NO_TEST')
    # use in_memory explicitly here because i haven't figured out how to pass arcgis environments to my functions :(
    fc_temp = 'in_memory/temp_merged'
    fcount1 = int(arcpy.GetCount_management(fc_temp).getOutput(0))
    cu.multi_msg(
        'Before selection and cleaning, feature count is {0}'.format(fcount1))
    if selection:
        cu.multi_msg('Selecting features...')
        arcpy.Select_analysis('temp_merged', 'in_memory/merged_select',
                              selection)
        fc_temp = 'in_memory/merged_select'

    fcount2 = int(arcpy.GetCount_management(fc_temp).getOutput(0))
    cu.multi_msg(
        'After selection and before cleaning, feature count is {0}'.format(
            fcount2))

    arcpy.CopyFeatures_management(fc_temp, out_fc)
Beispiel #13
0
def burn_streams(subregion_ned,
                 nhd_gdb,
                 burnt_out,
                 projection=arcpy.SpatialReference(102039)):
    env.snapRaster = subregion_ned
    env.outputCoordinateSystem = projection
    env.compression = "LZ77"  # compress temp tifs for speed
    env.extent = subregion_ned
    env.workspace = 'in_memory'

    flowline = os.path.join(nhd_gdb, 'NHDFlowline')
    # Copy flowlines to shapefile that will inherit environ output coord system
    # just easier to have a copy in the correct projection later
    arcpy.CopyFeatures_management(flowline, 'flowline_proj')
    ##    arcpy.FeatureClassToFeatureClass_conversion("NHDFlowline", "in_memory", flow_line)
    cu.multi_msg("Prepared NHDFlowline for rasterizing.")

    # Feature to Raster- rasterize the NHDFlowline
    # will inherit grid from env.snapRaster
    arcpy.FeatureToRaster_conversion('flowline_proj', "OBJECTID",
                                     'flowline_raster', "10")
    cu.multi_msg("Converted flowlines to raster.")

    # Raster Calculator- burns in streams, beveling in from 500m
    cu.multi_msg(
        "Burning streams into raster, 10m deep and beveling in from 500m out. This may take a while...."
    )
    arcpy.CheckOutExtension("Spatial")
    distance = EucDistance('flowline_proj', cell_size="10")
    streams = Reclassify(
        Raster('flowline_raster') > 0, "Value", "1 1; NoData 0")
    burnt = Raster(subregion_ned) - (10 * streams) - (0.02 * (500 - distance) *
                                                      (distance < 500))

    cu.multi_msg("Saving output raster...")
    burnt.save(burnt_out)

    # Delete intermediate rasters and shapefiles
    for item in ['flowline_proj', 'flowline_raster']:
        arcpy.Delete_management(item)
    arcpy.CheckInExtension("Spatial")
    cu.multi_msg("Burn process completed")
    arcpy.ResetEnvironments()
def unzip_ned(file_id, ned_dir, out_dir):
                # clunky but this works in USA: zipped files sometimes called
                # something like n36w87 instead of n36w087 so try all 3
                filename_variants = [os.path.join(ned_dir, f) for f in [file_id + ".zip",
                file_id[0:4] + file_id[5:] + ".zip",
                file_id[0] + file_id[2:] + ".zip"]]
                filename_to_use = ''
                for f in filename_variants:
                    if not os.path.exists(f):
                        continue
                    else:
                        filename_to_use = f

                if filename_to_use:
                    cu.multi_msg("Unzipping file %s" % filename_to_use)
                    zf = zipfile.ZipFile(filename_to_use)
                    zf.extractall(out_dir)
                    return True
                else:
                    cu.multi_msg("ERROR: A tile for %s does not exist in the specified location" % file_id)
                    return False
def InsideState(state, nwi, lakes, outfc):
    arcpy.env.outputCoordinateSystem = arcpy.SpatialReference(102039)

    # Select only wetlands with their center inside this state
    # This way all wetlands will only be represented once when we merge all states

    arcpy.env.workspace = 'in_memory'
    arcpy.MakeFeatureLayer_management(nwi, "nwi_lyr")
    cu.multi_msg('Selecting wetlands with their center in the state.')
    arcpy.SelectLayerByLocation_management("nwi_lyr", 'HAVE_THEIR_CENTER_IN', state,'','NEW_SELECTION')

    # Two things to make wetlands conform to the CSI definition
    # Select only palustrine systems that aren't freshwater ponds
    # and make it impossible for wetlands to be inside lakes
    wetland_type_field = arcpy.ListFields("nwi_lyr", "WETLAND_TY*")[0].name
    filter = """"ATTRIBUTE" LIKE 'P%' AND {} <> 'Freshwater Pond'""".format(wetland_type_field)
    cu.multi_msg("Selecting only palustrine wetlands...")
    arcpy.SelectLayerByAttribute_management("nwi_lyr", "SUBSET_SELECTION", filter)
    cu.multi_msg('Erasing lakes from wetlands layer.')
    arcpy.Erase_analysis("nwi_lyr", lakes, outfc)

    # Add two fields we will use, an ID field and the area in hectares
    arcpy.AddField_management(outfc, "WET_ID", "LONG")
    arcpy.CalculateField_management(outfc, "WET_ID", "!OBJECTID!", "PYTHON")

    arcpy.AddField_management(outfc, "AreaHa", "DOUBLE")
    arcpy.CalculateField_management(outfc, "AreaHa", "!shape.area@hectares!", "PYTHON")
Beispiel #16
0
def InsideState(state, nwi, lakes, outfc):
    arcpy.env.outputCoordinateSystem = arcpy.SpatialReference(102039)

    # Select only wetlands with their center inside this state
    # This way all wetlands will only be represented once when we merge all states

    arcpy.env.workspace = 'in_memory'
    arcpy.MakeFeatureLayer_management(nwi, "nwi_lyr")
    cu.multi_msg('Selecting wetlands with their center in the state.')
    arcpy.SelectLayerByLocation_management("nwi_lyr", 'HAVE_THEIR_CENTER_IN',
                                           state, '', 'NEW_SELECTION')

    # Two things to make wetlands conform to the CSI definition
    # Select only palustrine systems that aren't freshwater ponds
    # and make it impossible for wetlands to be inside lakes
    wetland_type_field = arcpy.ListFields("nwi_lyr", "WETLAND_TY*")[0].name
    filter = """"ATTRIBUTE" LIKE 'P%' AND {} <> 'Freshwater Pond'""".format(
        wetland_type_field)
    cu.multi_msg("Selecting only palustrine wetlands...")
    arcpy.SelectLayerByAttribute_management("nwi_lyr", "SUBSET_SELECTION",
                                            filter)
    cu.multi_msg('Erasing lakes from wetlands layer.')
    arcpy.Erase_analysis("nwi_lyr", lakes, outfc)

    # Add two fields we will use, an ID field and the area in hectares
    arcpy.AddField_management(outfc, "WET_ID", "LONG")
    arcpy.CalculateField_management(outfc, "WET_ID", "!OBJECTID!", "PYTHON")

    arcpy.AddField_management(outfc, "AreaHa", "DOUBLE")
    arcpy.CalculateField_management(outfc, "AreaHa", "!shape.area@hectares!",
                                    "PYTHON")
Beispiel #17
0
def burn(subregion_ned,
         nhd_gdb,
         burnt_out,
         projection=arcpy.SpatialReference(102039)):
    env.snapRaster = subregion_ned
    env.outputCoordinateSystem = projection
    env.compression = "LZ77"  # compress temp tifs for speed
    env.workspace = nhd_gdb

    # Copy flowlines to shapefile that will inherit environ output coord system
    flow_line = "NHDFlowline_Projected"
    if not arcpy.Exists(flow_line):
        arcpy.FeatureClassToFeatureClass_conversion("NHDFlowline", nhd_gdb,
                                                    flow_line)

    cu.multi_msg("Prepared NHDFlowline for rasterizing.")

    # Feature to Raster- rasterize the NHDFlowline
    flow_line_raster = "in_memory/flow_line_raster"
    arcpy.FeatureToRaster_conversion(flow_line, "OBJECTID", flow_line_raster,
                                     "10")
    cu.multi_msg("Converted flowlines to raster.")

    # Raster Calculator- burns in streams, beveling in from 500m
    cu.multi_msg(
        "Burning streams into raster, 10m deep and beveling in from 500m out. This may take a while...."
    )
    distance = EucDistance(flow_line, cell_size="10")
    streams = Reclassify(
        Raster(flow_line_raster) > 0, "Value", "1 1; NoData 0")
    burnt = Raster(subregion_ned) - (10 * streams) - (0.02 * (500 - distance) *
                                                      (distance < 500))

    cu.multi_msg("Saving output raster...")
    burnt.save(burnt_out)

    # Delete intermediate rasters and shapefiles
    cu.cleanup([flow_line, flow_line_raster])
    cu.multi_msg("Burn process completed")
def nhd_merge(gdb_list, example_feature_class_name, out_fc, selection = ''):
    arcpy.env.outputCoordinateSystem = arcpy.SpatialReference(102039)
    arcpy.env.workspace = 'in_memory'

    gdb_list = [os.path.join(gdb, os.path.basename(example_feature_class_name)) for gdb in gdb_list]
    gdb0 = gdb_list.pop(0)
    desc = arcpy.Describe(gdb0)
    cu.multi_msg('Merging all features together...')
    arcpy.CopyFeatures_management(gdb0, 'temp_merged')
    cu.lengthen_field('temp_merged', 'Permanent_Identifier', 255)
    arcpy.Append_management(gdb_list, 'temp_merged', 'NO_TEST')
    # use in_memory explicitly here because i haven't figured out how to pass arcgis environments to my functions :(
    if selection:
        cu.multi_msg('Selecting features...')
        arcpy.Select_analysis('temp_merged', 'in_memory/merged_select', selection)
    cu.multi_msg('Removing ID duplicates...')
    assumptions.remove_nhd_duplicates('in_memory/merged_select', 'Permanent_Identifier', 'in_memory/no_id_dupes')

    if desc.shapeType == 'Polygon':
        cu.multi_msg('Removing geographic duplicates and substantially overlapping features...')
        assumptions.remove_geographic_doubles('in_memory/no_id_dupes', out_fc, 'Permanent_Identifier', percent_overlap_allowed = 10)
    cu.multi_msg('nhd_merge complete.')
def burn_streams(subregion_ned, nhd_gdb, burnt_out,
                projection = arcpy.SpatialReference(102039)):
    env.snapRaster = subregion_ned
    env.outputCoordinateSystem = projection
    env.compression = "LZ77" # compress temp tifs for speed
    env.extent = subregion_ned
    env.workspace = 'in_memory'

    flowline = os.path.join(nhd_gdb, 'NHDFlowline')
    # Copy flowlines to shapefile that will inherit environ output coord system
    # just easier to have a copy in the correct projection later
    arcpy.CopyFeatures_management(flowline, 'flowline_proj')
##    arcpy.FeatureClassToFeatureClass_conversion("NHDFlowline", "in_memory", flow_line)
    cu.multi_msg("Prepared NHDFlowline for rasterizing.")

    # Feature to Raster- rasterize the NHDFlowline
    # will inherit grid from env.snapRaster
    arcpy.FeatureToRaster_conversion('flowline_proj', "OBJECTID",
                                    'flowline_raster', "10")
    cu.multi_msg("Converted flowlines to raster.")

    # Raster Calculator- burns in streams, beveling in from 500m
    cu.multi_msg("Burning streams into raster, 10m deep and beveling in from 500m out. This may take a while....")
    arcpy.CheckOutExtension("Spatial")
    distance = EucDistance('flowline_proj', cell_size = "10")
    streams = Reclassify(Raster('flowline_raster') > 0, "Value", "1 1; NoData 0")
    burnt = Raster(subregion_ned) - (10 * streams) - (0.02 * (500 - distance) * (distance < 500))

    cu.multi_msg("Saving output raster...")
    burnt.save(burnt_out)

    # Delete intermediate rasters and shapefiles
    for item in ['flowline_proj', 'flowline_raster']:
        arcpy.Delete_management(item)
    arcpy.CheckInExtension("Spatial")
    cu.multi_msg("Burn process completed")
    arcpy.ResetEnvironments()
Beispiel #20
0
def clip(raster, nhd_gdb, projection, outfolder):

    env.workspace = nhd_gdb
    env.outputCoordinateSystem = projection
    env.compression = "NONE"  # only final tifs are generated
    env.pyramid = "NONE"

    # Create a feature dataset in NHD file geodatabase named "HUC8_Albers" in Albers projection
    out_feature_dataset = "HUC8_Albers"
    arcpy.CreateFeatureDataset_management(env.workspace, out_feature_dataset,
                                          projection)
    arcpy.RefreshCatalog(nhd)

    # HUC8 polygons each saved as separate fc inheriting albers from environ
    huc8_fc = "WBD_HU8"
    field = "HUC_8"
    arcpy.MakeFeatureLayer_management(huc8_fc, "huc8_layer")

    with arcpy.da.SearchCursor(huc8_fc, field) as cursor:
        for row in cursor:
            if row[0].startswith(nhdsubregion):
                whereClause = ''' "%s" = '%s' ''' % (field, row[0])
                arcpy.SelectLayerByAttribute_management(
                    "huc8_layer", 'NEW_SELECTION', whereClause)
                arcpy.CopyFeatures_management(
                    "huc8_layer",
                    os.path.join(out_feature_dataset, "HUC" + row[0]))

    #retrieve only the single huc8 fcs and not the one with all of them
    fcs = arcpy.ListFeatureClasses("HUC%s*" % nhdsubregion, "Polygon",
                                   out_feature_dataset)
    fcs_buffered = [
        os.path.join(out_feature_dataset, fc + "_buffer") for fc in fcs
    ]
    out_clips = [
        os.path.join(outfolder, "huc8clips" + nhdsubregion,
                     "NED" + fc[3:] + ".tif") for fc in fcs
    ]

    # Buffer HUC8 feature classes by 5000m
    for fc, fc_buffered in zip(fcs, fcs_buffered):
        arcpy.Buffer_analysis(fc, fc_buffered, "5000 meters")

    cu.multi_msg("Created HUC8 buffers.")
    arcpy.RefreshCatalog(nhd)

    # Clips rasters
    cu.multi_msg("Starting HUC8 clips...")
    for fc_buffered, out_clip in zip(fcs_buffered, out_clips):
        arcpy.Clip_management(raster, '', out_clip, fc_buffered, "0",
                              "ClippingGeometry")

    arcpy.Compact_management(nhd)

    cu.multi_msg("Clipping complete.")
Beispiel #21
0
def clip(raster, nhd_gdb, projection, outfolder):

    env.workspace = nhd_gdb
    env.outputCoordinateSystem = projection
    env.compression = "NONE" # only final tifs are generated
    env.pyramid = "NONE"

    # Create a feature dataset in NHD file geodatabase named "HUC8_Albers" in Albers projection
    out_feature_dataset = "HUC8_Albers"
    arcpy.CreateFeatureDataset_management(env.workspace, out_feature_dataset, projection)
    arcpy.RefreshCatalog(nhd)

    # HUC8 polygons each saved as separate fc inheriting albers from environ
    huc8_fc = "WBD_HU8"
    field = "HUC_8"
    arcpy.MakeFeatureLayer_management(huc8_fc, "huc8_layer")

    with arcpy.da.SearchCursor(huc8_fc, field) as cursor:
        for row in cursor:
            if row[0].startswith(nhdsubregion):
                whereClause = ''' "%s" = '%s' ''' % (field, row[0])
                arcpy.SelectLayerByAttribute_management("huc8_layer", 'NEW_SELECTION', whereClause)
                arcpy.CopyFeatures_management("huc8_layer", os.path.join(out_feature_dataset, "HUC" + row[0]))

    #retrieve only the single huc8 fcs and not the one with all of them
    fcs = arcpy.ListFeatureClasses("HUC%s*" % nhdsubregion, "Polygon", out_feature_dataset)
    fcs_buffered = [os.path.join(out_feature_dataset, fc + "_buffer") for fc in fcs]
    out_clips = [os.path.join(outfolder, "huc8clips" + nhdsubregion,
    "NED" + fc[3:] + ".tif") for fc in fcs]

    # Buffer HUC8 feature classes by 5000m
    for fc, fc_buffered in zip(fcs, fcs_buffered):
        arcpy.Buffer_analysis(fc, fc_buffered, "5000 meters")

    cu.multi_msg("Created HUC8 buffers.")
    arcpy.RefreshCatalog(nhd)

    # Clips rasters
    cu.multi_msg("Starting HUC8 clips...")
    for fc_buffered, out_clip in zip(fcs_buffered, out_clips):
        arcpy.Clip_management(raster, '', out_clip, fc_buffered, "0", "ClippingGeometry")

    arcpy.Compact_management(nhd)

    cu.multi_msg("Clipping complete.")
def split_strahler(stream_area_fc, streams, out_area_fc):
    """This function splits up the NHDArea feature class, which does not
    start and stop polygons at confluences, by creating break points near the
    confluences to split up the polygons. Then, it adds the Strahler value from
    the stream centerline."""
    # 1) Generate euclidean allocation raster from streams (use OBJECTID)
    # 2) Convert euclidean allocation raster to polygons
    # 3) Join allocation polygons "gridcode" to streams "OBJECTID" so that
    #    Strahler value is attached to allocation polygon
    # 4) Use identity function to split up the StreamRiver polygons at the
    #    allocation polygon boundaries, and add the Strahler values
    old_workspace = env.workspace
    env.workspace = 'in_memory'
    cu.multi_msg("Splitting stream area polygons between confluences and joining 1) Strahler order to them...")
    cu.multi_msg('next messages for testing')
    arcpy.CheckOutExtension('Spatial')
    cu.multi_msg('euc')
    euc = EucAllocation(streams, cell_size = '50', source_field = 'OBJECTID')
    arcpy.CheckInExtension('Spatial')
    cu.multi_msg('conversion')
    arcpy.RasterToPolygon_conversion(euc, 'allocation_polys')
    stream_id_field = arcpy.ListFields(streams, 'Permanent_')[0].name
    cu.multi_msg('join')
    arcpy.JoinField_management('allocation_polys', 'grid_code', streams, 'OBJECTID', ['Strahler', 'LengthKm', stream_id_field])
    cu.multi_msg('identity')
    arcpy.Identity_analysis(stream_area_fc, 'allocation_polys', out_area_fc)
    env.workspace = old_workspace
    cu.multi_msg("Splitting strema area polygons finished.")
Beispiel #23
0
def mosaic(in_workspace,
           out_dir,
           available_ram=4,
           projection=arcpy.SpatialReference(102039)):

    # Set up environments
    env.terrainMemoryUsage = True
    env.compression = "LZ77"  # compress temp tifs for speed
    env.pyramids = "NONE"  # for intermediates only
    env.outputCoordinateSystem = projection

    env.workspace = in_workspace
    huc4_code = re.search('\d{4}', os.path.basename(in_workspace)).group()
    nhd_gdb = arcpy.ListWorkspaces()[0]

    # Select the right HUC4 from WBD_HU4 and make it it's own layer.
    wbd_hu4 = os.path.join(nhd_gdb, "WBD_HU4")
    if not arcpy.Exists(wbd_hu4):
        wbd_hu4 = os.path.join(nhd_gdb, "WBDHU4")
    arcpy.AddMessage(wbd_hu4)
    arcpy.AddMessage(arcpy.Exists(wbd_hu4))
    field_name = (arcpy.ListFields(wbd_hu4, "HU*4"))[0].name
    whereClause = """{0} = '{1}'""".format(
        arcpy.AddFieldDelimiters(nhd_gdb, field_name), huc4_code)
    arcpy.MakeFeatureLayer_management(wbd_hu4, "Subregion", whereClause)

    # Apply a 5000 meter buffer around subregion
    subregion_buffer = os.path.join(nhd_gdb, "Subregion_Buffered_5000m")
    arcpy.Buffer_analysis("Subregion", subregion_buffer, "5000 meters")
    cu.multi_msg("Buffered subregion.")

    # Walk through the folder with NEDs to make a list of rasters
    in_workspace = r'D:\Continental_Limnology\Data_Working\Test_Watersheds\NHD0503'
    mosaic_rasters = []
    for dirpath, dirnames, filenames in arcpy.da.Walk(
            in_workspace, datatype="RasterDataset"):
        for filename in filenames:
            print(filename)
            if not '.jpg' in filename:
                name = os.path.join(dirpath, filename)
                mosaic_rasters.append(name)

    cu.multi_msg("Found NED rasters.")

    # Update environments
    env.extent = subregion_buffer
    approx_size_dir_GB = len(mosaic_rasters) * .5

    if approx_size_dir_GB < .5 * int(available_ram):
        env.workspace = 'in_memory'
        memory_msg = (
            "Attempting to use in_memory workspace. If you" +
            " experience problems during the execution of this tool, " +
            "try running it again with a lower value " +
            "entered for 'Available RAM'.")
        cu.multi_msg(memory_msg)

    else:
        env.workspace = out_dir
    env.outputCoordinateSystem = mosaic_rasters[0]

    # Assign names to intermediate outputs in outfolder
    mosaic_unproj = "mosaic_t1"
    mosaic_proj = "mosaic_t2"

    # Mosaic, then project
    # Cannot do this in one step using MosaicToNewRaster's projection parameter
    # because you cannot set the cell size correctly
    cu.multi_msg("Creating initial mosaic. This may take a while...")

    arcpy.MosaicToNewRaster_management(mosaic_rasters, env.workspace,
                                       mosaic_unproj, "", "32_BIT_FLOAT", "",
                                       "1", "LAST")

    cu.multi_msg("Projecting mosaic...")

    arcpy.ProjectRaster_management(mosaic_unproj, mosaic_proj, projection,
                                   "BILINEAR", "10")

    #final mosaic environs
    env.pyramids = "PYRAMIDS -1 SKIP_FIRST"  # need to check outputs efficiently
    env.outputCoordinateSystem = projection
    cu.multi_msg("Clipping final mosaic...")

    out_mosaic = os.path.join(out_dir, "NED13_%s.tif" % huc4_code)
    arcpy.Clip_management(mosaic_proj, '', out_mosaic, subregion_buffer, "0",
                          "ClippingGeometry")

    # Clean up
    for item in [mosaic_unproj, mosaic_proj]:
        arcpy.Delete_management(item)
    cu.multi_msg("Mosaicked NED tiles and clipped to HUC4 extent.")

    for raster in mosaic_rasters:
        arcpy.Delete_management(raster)
Beispiel #24
0
def stage_files(nhd_gdb, ned_dir, ned_footprints_fc, out_dir, is_zipped):
    env.workspace = 'in_memory'

    #####################################################################
    cu.multi_msg("1) Creating Directory Structure and Copying NHD Geodatabase")
    #####################################################################
    #finds the 4-digit huc code in the filename
    huc4_code = re.search('\d{4}', os.path.basename(nhd_gdb)).group()

    out_subdir = os.path.join(out_dir, "NHD" + huc4_code)
    if not os.path.exists(out_subdir):
        os.mkdir(out_subdir)
    nhd_gdb_copy = os.path.join(out_subdir, os.path.basename(nhd_gdb))
    arcpy.Copy_management(nhd_gdb, nhd_gdb_copy)

    ####################################################################
    cu.multi_msg("2) Getting WBD Poly For Subregion and Buffering by 5000m...")
    #####################################################################

    #select only this subregion from the wbd layer in the nhd_gdb (bordering
    # subregions are included in there too) and buffer it
    wbd_hu4 = os.path.join(nhd_gdb_copy, "WBD_HU4")
    if not arcpy.Exists(wbd_hu4):
        wbd_hu4 = os.path.join(nhd_gdb_copy, "WBDHU4")
    field_name = (arcpy.ListFields(wbd_hu4, "HU*4"))[0].name
    whereClause = """{0} = '{1}'""".format(
        arcpy.AddFieldDelimiters(nhd_gdb_copy, field_name), huc4_code)
    arcpy.MakeFeatureLayer_management(wbd_hu4, "wbd_poly", whereClause)
    arcpy.Buffer_analysis("wbd_poly", "wbd_buf", "5000 meters")

    #####################################################################
    cu.multi_msg(
        "3) Clipping NED Tile polys from Buffered NHD Subregion From WBD...")
    #####################################################################
    arcpy.Clip_analysis(ned_footprints_fc, "wbd_buf", "ned_clip")

    #####################################################################
    cu.multi_msg(
        "4) Getting File_ID of clipped NED footprint polys and copying NED data to output location"
    )
    #####################################################################
    missing_NED_list = []
    with arcpy.da.SearchCursor("ned_clip", ["FILE_ID"]) as cursor:
        for row in cursor:
            file_id = row[0].replace("g", "")

            # unzipping if needed

            if is_zipped:
                unzipped_file = unzip_ned(file_id, ned_dir, out_subdir)
                if not unzipped_file:
                    missing_NED_list.append(file_id)
            else:

                # copy ned tiles to output location
                ned_source = os.path.join(ned_dir, file_id)
                ned_destination = os.path.join(out_subdir, file_id)
                if not os.path.exists(ned_source):
                    cu.multi_msg(
                        "ERROR: Tile %s does not exist in the specified location"
                        % file_id)
                    missing_NED_list.append(file_id)
                else:
                    if not os.path.exists(ned_destination):
                        shutil.copytree(ned_source, ned_destination)
                    else:
                        cu.multi_msg(
                            "Output folder for this NED tile already exists.")
    if missing_NED_list:
        warning_text = "WARNING: NED tiles did not exist for the following: %s" % ','.join(
            missing_NED_list)
        arcpy.AddWarning(warning_text)
        print(warning_text)
    for item in ["wbd_buf", "ned_clip"]:
        arcpy.Delete_management(item)
    return out_subdir
Beispiel #25
0
def nhd_merge(gdb_list, example_feature_class_name, out_fc, selection = ''):
    arcpy.env.outputCoordinateSystem = arcpy.SpatialReference(102039) # USA_Contiguous_Albers_Equal_Area_Conic_USGS_version
    arcpy.env.workspace = 'in_memory'

    gdb_list = [os.path.join(gdb, os.path.basename(example_feature_class_name)) for gdb in gdb_list]
    gdb0 = gdb_list.pop(0)
    desc = arcpy.Describe(gdb0)
    cu.multi_msg('Merging all features together...')
    arcpy.CopyFeatures_management(gdb0, 'temp_merged')
    cu.lengthen_field('temp_merged', 'Permanent_Identifier', 255)
    cu.merge_many(gdb_list, 'in_memory/the_rest_merged')
    arcpy.Append_management('in_memory/the_rest_merged', 'temp_merged', 'NO_TEST')
    # use in_memory explicitly here because i haven't figured out how to pass arcgis environments to my functions :(
    fc_temp = 'in_memory/temp_merged'
    fcount1 = int(arcpy.GetCount_management(fc_temp).getOutput(0))
    cu.multi_msg('Before selection and cleaning, feature count is {0}'.format(fcount1))
    if selection:
        cu.multi_msg('Selecting features...')
        arcpy.Select_analysis('temp_merged', 'in_memory/merged_select', selection)
        fc_temp = 'in_memory/merged_select'

    fcount2 = int(arcpy.GetCount_management(fc_temp).getOutput(0))
    cu.multi_msg('After selection and before cleaning, feature count is {0}'.format(fcount2))

    cu.multi_msg('Removing complete duplicates...')
    fc_temp_fields = [f.name for f in arcpy.ListFields(fc_temp) if f.type <> 'OID']
    arcpy.DeleteIdentical_management(fc_temp, fields = [f.name for f in arcpy.ListFields(fc_temp) if f.type <> 'OID'])

    fcount3 = int(arcpy.GetCount_management(fc_temp).getOutput(0))
    cu.multi_msg('After removing complete duplicates only, feature count is {0}'.format(fcount3))

    cu.multi_msg('Removing remaining ID duplicates...')
    assumptions.remove_nhd_duplicates(fc_temp, 'Permanent_Identifier', 'in_memory/no_id_dupes')

    fcount4 = int(arcpy.GetCount_management(fc_temp).getOutput(0))
    cu.multi_msg('After removing all ID duplicates, feature count is {0}'.format(fcount4))

    if desc.shapeType == 'Polygon':
        cu.multi_msg('Removing geographic duplicates and substantially overlapping features...')
        assumptions.remove_geographic_doubles('in_memory/no_id_dupes', out_fc, 'Permanent_Identifier', percent_overlap_allowed = 10)
    cu.multi_msg('nhd_merge complete.')

    fcount5 = int(arcpy.GetCount_management(fc_temp).getOutput(0))
    cu.multi_msg('Final feature count is {0}'.format(fcount5))
def mosaic(in_workspace, out_dir, available_ram = 4, projection = arcpy.SpatialReference(102039)) :

    # Set up environments
    env.terrainMemoryUsage = True
    env.compression = "LZ77" # compress temp tifs for speed
    env.pyramids = "NONE" # for intermediates only
    env.outputCoordinateSystem = projection

    env.workspace = in_workspace
    huc4_code = re.search('\d{4}', os.path.basename(in_workspace)).group()
    nhd_gdb = os.path.join(in_workspace, 'NHDH%s.gdb' % huc4_code)

    # Select the right HUC4 from WBD_HU4 and make it it's own layer.
    wbd_hu4 = os.path.join(nhd_gdb, "WBD_HU4")
    field_name = (arcpy.ListFields(wbd_hu4, "HU*4"))[0].name
    whereClause =  """{0} = '{1}'""".format(arcpy.AddFieldDelimiters(nhd_gdb, field_name), huc4_code)
    arcpy.MakeFeatureLayer_management(wbd_hu4, "Subregion", whereClause)

    # Apply a 5000 meter buffer around subregion
    subregion_buffer = os.path.join(nhd_gdb, "Subregion_Buffered_5000m")
    arcpy.Buffer_analysis("Subregion", subregion_buffer, "5000 meters")
    cu.multi_msg("Buffered subregion.")

    # Walk through the folder with NEDs to make a list of rasters
    mosaic_rasters = []
    for dirpath, dirnames, filenames in arcpy.da.Walk(in_workspace, datatype="RasterDataset"):
        for filename in filenames:
            print(filename)
            if filename.startswith('grd'):
                name = os.path.join(dirpath, filename)
                mosaic_rasters.append(name)


    cu.multi_msg("Found NED ArcGrids.")

    # Update environments
    env.extent = subregion_buffer
    approx_size_dir_GB = len(mosaic_rasters) * .5

    if approx_size_dir_GB < .5 * int(available_ram):
        env.workspace = 'in_memory'
        memory_msg = ("Attempting to use in_memory workspace. If you " +
                    " experience problems during the exectuion of this tool, " +
                    "try running it again with a lower value " +
                    "entered for 'Available RAM'.")
        cu.multi_msg(memory_msg)

    else:
        env.workspace = out_dir
    env.outputCoordinateSystem = mosaic_rasters[0]

    # Assign names to intermediate outputs in outfolder
    mosaic_unproj = "mosaic_t1"
    mosaic_proj = "mosaic_t2"

    # Mosaic, then project
    # Cannot do this in one step using MosaicToNewRaster's projection parameter
    # because you cannot set the cell size correctly
    cu.multi_msg("Creating initial mosaic. This may take a while...")

    arcpy.MosaicToNewRaster_management(mosaic_rasters, env.workspace,
    mosaic_unproj, "", "32_BIT_FLOAT", "", "1", "LAST")

    cu.multi_msg("Projecting mosaic...")

    arcpy.ProjectRaster_management(mosaic_unproj, mosaic_proj,
    projection, "BILINEAR", "10")

    #final mosaic environs
    env.pyramids = "PYRAMIDS -1 SKIP_FIRST" # need to check outputs efficiently
    env.outputCoordinateSystem = projection
    cu.multi_msg("Clipping final mosaic...")

    out_mosaic = os.path.join(out_dir, "NED13_%s.tif" % huc4_code)
    arcpy.Clip_management(mosaic_proj, '', out_mosaic, subregion_buffer,
     "0", "ClippingGeometry")

    # Clean up
    for item in [mosaic_unproj, mosaic_proj]:
        arcpy.Delete_management(item)
    cu.multi_msg("Mosaicked NED tiles and clipped to HUC4 extent.")
def polygons_in_zones(zone_fc,
                      zone_field,
                      polygons_of_interest,
                      output_table,
                      interest_selection_expr,
                      contrib_area=True):
    old_workspace = arcpy.env.workspace
    arcpy.env.workspace = 'in_memory'
    arcpy.env.outputCoordinateSystem = arcpy.SpatialReference(102039)

    temp_polyzones = cu.create_temp_GDB('temp_polyzones')
    selected_polys = os.path.join(temp_polyzones, 'selected_polys')
    cu.multi_msg('Copying/selecting polygon features...')
    if interest_selection_expr:
        arcpy.Select_analysis(polygons_of_interest, selected_polys,
                              interest_selection_expr)
    else:
        arcpy.CopyFeatures_management(polygons_of_interest, selected_polys)

    arcpy.AddField_management(selected_polys, 'POLYAREA_ha', 'DOUBLE')
    arcpy.CalculateField_management(selected_polys, 'POLYAREA_ha',
                                    '!shape.area@hectares!', 'PYTHON')

    # use tabulate intersection for the areas overlapping
    tab_table = 'tabulate_intersection_table'
    cu.multi_msg('Tabulating intersection between zones and polygons...')
    arcpy.TabulateIntersection_analysis(zone_fc, zone_field, selected_polys,
                                        tab_table)

    # area was calculated in map units which was m2 so convert to hectares
    arcpy.AddField_management(tab_table, 'Poly_Overlapping_AREA_ha', 'DOUBLE')
    arcpy.CalculateField_management(tab_table, 'Poly_Overlapping_AREA_ha',
                                    '!AREA!/10000', 'PYTHON')

    # just change the name of the percent field
    cu.rename_field(tab_table, 'PERCENTAGE', 'Poly_Overlapping_AREA_pct', True)
    spjoin_fc = 'spatial_join_output'

    # Spatial join for the count and contributing area
    fms = arcpy.FieldMappings()

    fm_zone_id = arcpy.FieldMap()
    fm_zone_id.addInputField(zone_fc, zone_field)

    fm_count = arcpy.FieldMap()
    fm_count.addInputField(selected_polys, 'POLYAREA_ha')
    count_name = fm_count.outputField
    count_name.name = 'Poly_Count'
    count_name.alias = 'Poly_Count'
    fm_count.outputField = count_name
    fm_count.mergeRule = 'Count'

    fm_contrib_area = arcpy.FieldMap()
    fm_contrib_area.addInputField(selected_polys, 'POLYAREA_ha')
    contrib_area_name = fm_contrib_area.outputField
    contrib_area_name.name = 'Poly_Contributing_AREA_ha'
    contrib_area_name.alias = 'Poly_Contributing_AREA_ha'
    fm_contrib_area.outputField = contrib_area_name
    fm_contrib_area.mergeRule = 'Sum'

    fms.addFieldMap(fm_zone_id)
    fms.addFieldMap(fm_count)
    fms.addFieldMap(fm_contrib_area)

    cu.multi_msg('Spatial join between zones and wetlands...')
    arcpy.SpatialJoin_analysis(zone_fc, selected_polys, spjoin_fc,
                               "JOIN_ONE_TO_ONE", "KEEP_ALL", fms, "INTERSECT")

    cu.multi_msg('Refining output...')
    arcpy.JoinField_management(tab_table, zone_field, spjoin_fc, zone_field,
                               ["Poly_Count", "Poly_Contributing_AREA_ha"])
    final_fields = [
        'Poly_Overlapping_AREA_ha', 'Poly_Overlapping_AREA_pct', 'Poly_Count',
        'Poly_Contributing_AREA_ha'
    ]

    # make output nice
    cu.one_in_one_out(tab_table, final_fields, zone_fc, zone_field,
                      output_table)
    cu.redefine_nulls(output_table, final_fields, [0, 0, 0, 0])

    # clean up
    for item in [selected_polys, tab_table, spjoin_fc]:
        arcpy.Delete_management(item)
    arcpy.Delete_management(temp_polyzones)
    arcpy.env.workspace = old_workspace

    cu.multi_msg('Polygons in zones tool complete.')
def stats_area_table(zone_fc, zone_field, in_value_raster, out_table, is_thematic):
    arcpy.CheckOutExtension("Spatial")
    cu.multi_msg("Calculating zonal statistics...")
    temp_zonal_table = 'in_memory/zonal_stats_temp'
    temp_entire_table = 'in_memory/temp_entire_table'

    # calculate/doit
    env.snapRaster = in_value_raster
    env.cellSize = in_value_raster

    # this has to be on disk for some reason to avoid background processing
    # errors thrown up at random
    # hence we get the following awkward horribleness
    use_convert_raster = False
    try:
        arcpy.sa.ZonalStatisticsAsTable(zone_fc, zone_field, in_value_raster,
                                temp_zonal_table, 'DATA', 'ALL')
    # with Permanent_Identifier as the zone_field, background processing errors
    # and another error get thrown up at random
    # it's faster to do zonal stats as above but if it fails (which it does
    # pretty quickly, usually), do this way which always works but takes
    # twice as long on large rasters
    except:
        use_convert_raster = True
        temp_workspace = cu.create_temp_GDB('temp_zonal')
        convert_raster = os.path.join(temp_workspace,
                        cu.shortname(zone_fc) + '_converted')
        cu.multi_msg('Creating raster {0}'.format(convert_raster))
        arcpy.PolygonToRaster_conversion(zone_fc, zone_field, convert_raster)
        arcpy.sa.ZonalStatisticsAsTable(convert_raster, zone_field, in_value_raster,
                                    temp_zonal_table, "DATA", "ALL")

    if is_thematic:
        #for some reason env.celLSize doesn't work
        desc = arcpy.Describe(in_value_raster)
        cell_size = desc.meanCelLHeight

        # calculate/doit
        temp_area_table = 'in_memory/tab_area_temp'
        cu.multi_msg("Tabulating areas...")

        if use_convert_raster:
            arcpy.sa.TabulateArea(convert_raster, zone_field, in_value_raster,
                                'Value', temp_area_table, cell_size)
        else:
            arcpy.sa.TabulateArea(zone_fc, zone_field, in_value_raster,
                                'Value', temp_area_table, cell_size)

        # making the output table
        arcpy.CopyRows_management(temp_area_table, temp_entire_table)
        zonal_stats_fields = ['AREA']
        arcpy.JoinField_management(temp_entire_table, zone_field, temp_zonal_table, zone_field, zonal_stats_fields)

        # cleanup
        arcpy.Delete_management(temp_area_table)

    if not is_thematic:
        # making the output table
        arcpy.CopyRows_management(temp_zonal_table, temp_entire_table)

    cu.multi_msg("Refining output table...")
    refine_zonal_output(temp_entire_table, is_thematic)



    #final table gets a record even for no-data zones
    keep_fields = [f.name for f in arcpy.ListFields(temp_entire_table)]
    if zone_field.upper() in keep_fields:
        keep_fields.remove(zone_field.upper())
    if zone_field in keep_fields:
        keep_fields.remove(zone_field)
    cu.one_in_one_out(temp_entire_table, keep_fields, zone_fc, zone_field, out_table)
##    cu.redefine_nulls(out_table, keep_fields, ["NA"]* len(keep_fields))

    # count whether all zones got an output record or not)
    out_count = int(arcpy.GetCount_management(temp_entire_table).getOutput(0))
    in_count = int(arcpy.GetCount_management(zone_fc).getOutput(0))
    if out_count < in_count:
        warn_msg = ("WARNING: {0} features are missing in the output table"
                    " because they are too small for this raster's"
                    " resolution. This may be okay depending on your"
                    " application.").format(in_count - out_count)
        arcpy.AddWarning(warn_msg)
        print(warn_msg)

    # cleanup
    arcpy.Delete_management(temp_zonal_table)
    arcpy.Delete_management(temp_entire_table)
    if use_convert_raster:
        arcpy.Delete_management(os.path.dirname(temp_workspace))
    arcpy.CheckInExtension("Spatial")
def line_density(zones, zonefield, lines, out_table, interest_selection_expr):
    # Make output folder
##    name = "LineDensity_" + os.path.splitext(os.path.basename(zones))[0]
##    outfolder = os.path.join(topoutfolder, name)
##    if not os.path.exists(outfolder):
##        os.mkdir(outfolder)

    # Environmental Settings
    ws = "in_memory"

    if interest_selection_expr:
        arcpy.MakeFeatureLayer_management(lines, "selected_lines", interest_selection_expr)
    else:
        arcpy.MakeFeatureLayer_management(lines, "selected_lines")

    arcpy.env.workspace = ws
    albers = arcpy.SpatialReference(102039)
    arcpy.env.outputCoordinateSystem = albers
    arcpy.env.extent = zones

    # Zones will be coerced to albers, have to check lines though
    arcpy.CopyFeatures_management(zones, "zones_temp")
    if arcpy.Describe(lines).spatialReference.factoryCode != albers.factoryCode:
        arcpy.AddError("Lines feature class does not have desired projection (Albers USGS). Re-project to factory code 102039 and try again.")
        sys.exit(1)

    # Add hectares field to zones
    arcpy.AddField_management("zones_temp", "ZoneAreaHa", "DOUBLE")
    arcpy.CalculateField_management("zones_temp", "ZoneAreaHa", "!shape.area@hectares!", "PYTHON")

    # Perform identity analysis to join fields and crack lines at polygon boundaries
    cu.multi_msg("Cracking lines at polygon boundaries...")
    arcpy.Identity_analysis("selected_lines", "zones_temp", "lines_identity")
    cu.multi_msg("Cracking lines complete.")

    # Recalculate lengths
    arcpy.AddField_management("lines_identity", "LengthM", "DOUBLE")
    arcpy.CalculateField_management("lines_identity", "LengthM", '!shape.length@meters!', "PYTHON")

    # Summarize statistics by zone
    arcpy.Statistics_analysis("lines_identity", "length_in_zone", "LengthM SUM", zonefield)


    # Join ZoneAreaHa to table
    arcpy.JoinField_management("length_in_zone", zonefield, "zones_temp" , zonefield, "ZoneAreaHa")

    # Delete rows in table with zero for zone area
##    with arcpy.da.UpdateCursor("length_in_zone", "ZoneAreaHa") as cursor:
##        for row in cursor:
##            if row[0] is None:
##                cursor.deleteRow()

    # Add Density field and calc
    arcpy.AddField_management("length_in_zone", "Density_MperHA", "DOUBLE",'','','','',"NULLABLE")
    exp = "!SUM_LengthM! / !ZONEAREAHA!"
    arcpy.CalculateField_management("length_in_zone", "Density_MperHA", exp, "PYTHON")

    cu.one_in_one_out("length_in_zone", ['SUM_LengthM', 'Density_MperHA'], zones, zonefield, out_table)
    cu.redefine_nulls(out_table, ['SUM_LengthM', 'Density_MperHA'], [0, 0])


##    # Join to the original table
##    keep_fields = ["ZoneID", "SUM_LengthM", "Density_MperHA"]
##    arcpy.JoinField_management('zones_temp', zonefield, "length_in_zone", zonefield, keep_fields[1:])
##
##    # Select only null records and change to 0
##    arcpy.MakeFeatureLayer_management('zones_temp', 'zones_temp_lyr')
##    arcpy.SelectLayerByAttribute_management('zones_temp_lyr', "NEW_SELECTION", '''"SUM_LengthM" is null''')
##    fields_to_calc = ["SUM_LengthM", "Density_MperHA"]
##    for f in fields_to_calc:
##        arcpy.CalculateField_management('zones_temp_lyr', f, 0, "PYTHON")
##
##    #Delete all the fields that aren't the ones I need
##    keep_fields = ["ZoneID", "SUM_LengthM", "Density_MperHA"]
##    all_fields = [f.name for f in arcpy.ListFields('zones_temp_lyr')]
##    for f in all_fields:
##        if f not in keep_fields:
##            try:
##                arcpy.DeleteField_management('zones_temp_lyr', f)
##            except:
##                continue
##    arcpy.SelectLayerByAttribute_management('zones_temp_lyr', 'CLEAR_SELECTION')
##
##    arcpy.CopyRows_management('zones_temp_lyr', out_table)

    for tempitem in ['zones_temp', 'lines_identity', 'length_in_zone']:
        arcpy.Delete_management(tempitem)

    return out_table
Beispiel #30
0
def upstream_lakes(nhd_gdb, output_table):
    #define paths in nhd geodatabase
    nhd_waterbody = os.path.join(nhd_gdb, 'NHDWaterbody')
    nhd_flowline = os.path.join(nhd_gdb, 'NHDFlowline')
    nhd_junctions = os.path.join(nhd_gdb, 'HYDRO_NET_Junctions')
    hydro_net = os.path.join(nhd_gdb, 'Hydrography', 'HYDRO_NET')

    arcpy.env.workspace = 'in_memory'
    arcpy.env.outputCoordinateSystem = arcpy.SpatialReference(102039)

    arcpy.MakeFeatureLayer_management(nhd_junctions, 'junctions')

    cu.multi_msg('Preparing layers and fields for calculations....')

    # select only lakes as defined in our project: waterbodies with one of these
    # types and greater than 4ha, and certain reservoirs greater than 10ha
    fcodes = (39000, 39004, 39009, 39010, 39011, 39012, 43600, 43613, 43615,
              43617, 43618, 43619, 43621)
    gte_4ha_lakes_query = '''("AreaSqKm" >=0.04 AND "FCode" IN %s) OR ("FCode" = 43601 AND "AreaSqKm" >= 0.1)''' % (
        fcodes, )
    gte_10ha_lakes_query = '''("AreaSqKm" >=0.1 AND "FCode" IN %s) OR ("FCode" = 43601 AND "AreaSqKm" >= 0.1)''' % (
        fcodes, )

    arcpy.MakeFeatureLayer_management(nhd_waterbody, 'gte_4ha_lakes',
                                      gte_4ha_lakes_query)
    arcpy.MakeFeatureLayer_management(nhd_waterbody, 'gte_10ha_lakes',
                                      gte_10ha_lakes_query)
    arcpy.CopyRows_management('gte_4ha_lakes', 'output_table')

    # (need this for line 61 make feature layer to work right!)
    arcpy.CopyFeatures_management('gte_4ha_lakes', 'gte_4ha_lakes_DISK')

    # add the new fields we'll calculate: count and area
    count_fields = ['Upstream_Lakes_4ha_Count', 'Upstream_Lakes_10ha_Count']
    area_fields = ['Upstream_Lakes_4ha_Area_ha', 'Upstream_Lakes_10ha_Area_ha']
    for cf in count_fields:
        arcpy.AddField_management('output_table', cf, 'LONG')
    for af in area_fields:
        arcpy.AddField_management('output_table', af, 'DOUBLE')
    new_fields = count_fields + area_fields

    # for each lake, use its junctions as input flags to the upstream trace, then
    # evalute the traced network for how many lakes are in it
    cu.multi_msg(
        "Calculating upstream network for each lake. This may take a few minutes, or a few hours..."
    )
    with arcpy.da.UpdateCursor('output_table', ['Permanent_Identifier'] +
                               new_fields) as cursor:
        for row in cursor:
            id = row[0]

            # get the junction points on top of this lake, can be 0 or several
            where_clause = """"{0}" = '{1}'""".format('Permanent_Identifier',
                                                      id)
            arcpy.MakeFeatureLayer_management('gte_4ha_lakes_DISK',
                                              "this_lake", where_clause)
            arcpy.SelectLayerByLocation_management('junctions', "INTERSECT",
                                                   "this_lake", "1 Meters")
            count_jxns = int(
                arcpy.GetCount_management('junctions').getOutput(0))

            # if the lake has no upstream connectivity whatsoever, it will have
            # no junctions on top of it. assign 0s
            # to the counts of upstream lakes. This can either be an "isolated"
            # lake or a "headwater" lake
            if count_jxns <= 0:
                row[1:] = [0, 0, 0, 0]

            # if there are any junctions, go ahead and trace upstream
            else:
                arcpy.CopyFeatures_management("junctions", 'this_lake_jxns')
                arcpy.TraceGeometricNetwork_management(hydro_net, "upstream",
                                                       'this_lake_jxns',
                                                       "TRACE_UPSTREAM")
                arcpy.SelectLayerByLocation_management('gte_4ha_lakes',
                                                       "INTERSECT",
                                                       "upstream/NHDFlowline",
                                                       '1 Meters',
                                                       'NEW_SELECTION')
                # Remove this lake from the selection!!
                arcpy.SelectLayerByAttribute_management(
                    'gte_4ha_lakes', 'REMOVE_FROM_SELECTION', where_clause)
                count_4ha_lakes = int(
                    arcpy.GetCount_management('gte_4ha_lakes').getOutput(0))

                # if there is upstream connectivity but no countable lakes
                # assign 0s for all the counts/areas and do not try to copy rows
                if count_4ha_lakes <= 0:
                    row[1:] = [0, 0, 0, 0]

                # if there are any lakes upstream, copy rows before doing
                # counts so we can use a search cursor on just these lakes
                else:
                    arcpy.CopyRows_management('gte_4ha_lakes',
                                              'these_4ha_lakes')

                    # row[1] is 4ha_Upstream_Lakes_Count
                    # row [3] is 4ha_Upstream_Lakes_Area
                    row[1] = int(
                        arcpy.GetCount_management('these_4ha_lakes').getOutput(
                            0))
                    total_area = 0
                    with arcpy.da.SearchCursor('these_4ha_lakes',
                                               ['AreaSqKm']) as area4_cursor:
                        for area4_row in area4_cursor:
                            total_area += area4_row[0] * 100
                    row[3] = total_area

                    # same but for 10ha
                    arcpy.SelectLayerByLocation_management(
                        'gte_10ha_lakes', "INTERSECT", "upstream/NHDFlowline",
                        '1 Meters', 'NEW_SELECTION')
                    arcpy.SelectLayerByAttribute_management(
                        'gte_10ha_lakes', 'REMOVE_FROM_SELECTION',
                        where_clause)
                    count_10ha_lakes = int(
                        arcpy.GetCount_management('gte_10ha_lakes').getOutput(
                            0))

                    # don't try to copy rows if selection is empty
                    if count_10ha_lakes <= 0:
                        row[2] = 0
                        row[4] = 0
                    # if there are features selected, copy rows so we
                    # can use a search cursor
                    else:
                        arcpy.CopyRows_management('gte_10ha_lakes',
                                                  'these_10ha_lakes')
                        row[2] = int(
                            arcpy.GetCount_management(
                                'these_10ha_lakes').getOutput(0))
                        total_area = 0
                        with arcpy.da.SearchCursor(
                                'these_4ha_lakes',
                            ['AreaSqKm']) as area10_cursor:
                            for area10_row in area10_cursor:
                                total_area += area10_row[0] * 100
                        row[4] = total_area
            cursor.updateRow(row)

            # delete intermediates before next iteration
            for item in [
                    'this_lake', 'this_lake_jxns', 'upstream',
                    'these_4ha_lakes', 'these_10ha_lakes'
            ]:
                try:
                    arcpy.Delete_management(item)
                except:
                    continue

    # clean up the output table
    all_fields = [f.name for f in arcpy.ListFields('output_table')]
    for f in all_fields:
        if f not in ['Permanent_Identifier'] + new_fields:
            try:
                arcpy.DeleteField_management('output_table', f)
            except:
                continue

    # write out the final file and clean up intermediates
    arcpy.CopyRows_management('output_table', output_table)
    for item in [
            'junctions', 'gte_4ha_lakes', 'gte_10ha_lakes', 'output_table'
    ]:
        arcpy.Delete_management(item)
def streams_in_zones(zones_fc, zone_field, streams_fc, output_table):

    arcpy.env.workspace = 'in_memory'

    # this bit enforces the correct ftype restriction just in case
    # geodata doesn't have this filtered already
    # this won't be as good as doing it before hand because it's
    # not sophisticated enough to take out the artificial lines going
    # through lakes
    need_selection = False
    with arcpy.da.SearchCursor(streams_fc, ["FType"]) as cursor:
        for row in cursor:
            if row[0] == 566:
                need_selection = True

    if need_selection:
        whereClause = """"FType" <> 566"""
        arcpy.Select_analysis(temp_lakes, "no_coastlines", whereClause)
        streams_fc = os.path.join(arcpy.env.workspace, "no_coastlines")

    selections = [
        '',
        """"Strahler" <= 3""",
        """"Strahler" > 3 AND "Strahler" <= 6""",
        """"Strahler" > 6""",
    ]
    temp_tables = ['Streams', 'Headwaters', 'Midreaches', 'Rivers']

    for sel, temp_table in zip(selections, temp_tables):
        cu.multi_msg(
            "Creating temporary table called {0} for streams where {1}".format(
                temp_table, sel))
        LineDensity.line_density(zones_fc, zone_field, streams_fc, temp_table,
                                 sel)
        new_fields = ['SUM_LengthM', 'Density_MperHA']
        for f in new_fields:
            cu.rename_field(temp_table, f, temp_table + '_' + f, True)

    # join em up and copy to final
    temp_tables.remove('Streams')
    for t in temp_tables:
        try:
            arcpy.JoinField_management('Streams', zone_field, t, zone_field)
        #sometimes there's no table if it was an empty selection
        except:
            empty_fields = [temp_table + '_' + f for f in new_fields]
            for ef in empty_fields:
                arcpy.AddField_management('Streams', ef, 'Double')
                arcpy.CalculateField_management('Streams', ef, '0', 'PYTHON')
            continue

    # remove all the extra zoneID fields, which have underscore in name
    drop_fields = [
        f.name for f in arcpy.ListFields('Streams', zone_field + '_*')
    ]
    for f in drop_fields:
        arcpy.DeleteField_management('Streams', f)
    arcpy.CopyRows_management('Streams', output_table)

    # clean up
    for item in ['Streams', 'no_coastlines'] + temp_tables:
        try:
            arcpy.Delete_management(item)
        except:
            continue
def create_csi_watersheds(flowdir, pour_dir, nhd_gdb, out_gdb):

    # Starting environmental variables:
    env.extent = flowdir
    env.snapRaster = flowdir
    env.cellSize = 10
    env.outputCoordinateSystem = arcpy.SpatialReference(102039)
    arcpy.CheckOutExtension('Spatial')

    huc8_code = re.search('\d{8}', os.path.basename(flowdir)).group()
    huc4_code = re.search('\d{4}', os.path.basename(nhd_gdb)).group()

    # create temp directory because we need shape geometry
    temp_gdb = cu.create_temp_GDB('watersheds' + huc4_code)
    cu.multi_msg("Temp geodatabase is located at {}".format(temp_gdb))
    env.workspace = temp_gdb

    wbd_hu8 = os.path.join(nhd_gdb, "WBD_HU8")
    field_name = (arcpy.ListFields(wbd_hu8, "HU*8"))[0].name
    whereClause8 = """{0} = '{1}'""".format(
        arcpy.AddFieldDelimiters(nhd_gdb, field_name), huc8_code)
    arcpy.Select_analysis(wbd_hu8, "hu8", whereClause8)
    arcpy.Buffer_analysis("hu8", "hu8_buffered", "100 meters")

    # Create the basic watersheds
    pour_points = os.path.join(pour_dir, 'pour_points.tif')
    arcpy.Clip_management(pour_points, '', "pour_points_clipped",
                          "hu8_buffered", '0', 'ClippingGeometry')
    raw_watersheds = os.path.join(
        out_gdb, 'huc{}_watersheds_precursors'.format(huc8_code))
    cu.multi_msg("Calculating preliminary watersheds...")
    outWatershed = Watershed(flowdir, "pour_points_clipped")
    outWatershed.save(raw_watersheds)

    cu.multi_msg(
        "Clipping watersheds to subregion boundaries and filtering spurious watersheds..."
    )

    # Watershed raster to polygons
    arcpy.RasterToPolygon_conversion(raw_watersheds, "wspoly1", 'NO_SIMPLIFY',
                                     "Value")

    # Clip watershed polygons to subregion polys.
    arcpy.Clip_analysis("wspoly1", "hu8", "wsclip1")

    # Clip watershed

    ##    # Calculate hectares
    ##    arcpy.AddField_management("wsclip1", "HA", "DOUBLE")
    ##    arcpy.CalculateField_management("wsclip1", "HA", '''!shape.area@hectares!''', "PYTHON")

    # Create fc of watershed polygons >= 1 ha that coincide with seed lines and polys.
    seedline = os.path.join(pour_dir, 'pourpoints.gdb', 'eligible_flowlines')
    seedpoly = os.path.join(pour_dir, 'pourpoints.gdb', 'eligible_lakes')
    arcpy.MakeFeatureLayer_management("wsclip1", "wsclip1_lyr")

    arcpy.SelectLayerByLocation_management("wsclip1_lyr", "INTERSECT",
                                           seedline, '', "NEW_SELECTION")
    arcpy.SelectLayerByLocation_management("wsclip1_lyr", "INTERSECT",
                                           seedpoly, '', "ADD_TO_SELECTION")
    arcpy.SelectLayerByAttribute_management("wsclip1_lyr", "SUBSET_SELECTION",
                                            '''"Shape_Area" >= 10000''')

    cu.multi_msg("Reshaping watersheds...")
    # Polygon back to raster
    grid_code = arcpy.ListFields("wsclip1_lyr", "grid*")[0].name
    arcpy.PolygonToRaster_conversion("wsclip1_lyr", grid_code, "ws_legit_ras")
    arcpy.Clip_management("ws_legit_ras", '', "ws_legit_clipped_ras", "hu8",
                          "0", "ClippingGeometry")

    # Make a raster from the subregion (boundary) polygon with zero for cell values.
    arcpy.AddField_management("hu8", "Value", "SHORT")
    arcpy.CalculateField_management("hu8", "Value", "0", "PYTHON")
    arcpy.PolygonToRaster_conversion("hu8", "Value", "boundary_raster")
    arcpy.Clip_management("boundary_raster", '', "boundary_raster_clip", "hu8",
                          '', "ClippingGeometry")

    # Fill NoData in watersheds with the zero values from the subregion raster's cells.
    composite = Con(IsNull("ws_legit_clipped_ras"), "boundary_raster_clip",
                    "ws_legit_clipped_ras")
    composite.save("composite_raster")
    arcpy.Clip_management("composite_raster", '', "composite_raster_clip",
                          "hu8", '0', "ClippingGeometry")

    # Make a mask of zero cells. NoData cells are the actual mask for nibble.
    premask = Con(IsNull("composite_raster_clip"), "composite_raster_clip", 0)
    premask.save("premask")

    arcpy.Clip_management("premask", '', "mask", "hu8", '', "ClippingGeometry")

    # Set Null to 1.
    pre_watersheds = Con(IsNull("composite_raster_clip"), 1,
                         "composite_raster_clip")
    pre_watersheds.save("pre_watersheds")  # does this speed things up?
    ##    prews.save("prews.tif")

    # Nibble masked values (null values along boundary).
    cu.multi_msg('Nibbling watersheds as part of reshaping...')
    nibble = Nibble("pre_watersheds", "mask", "DATA_ONLY")
    nibble.save("nibble")
    # Use HU8 buffer so that watersheds will overrun HU8 boundaries and get
    # clipped without weird slivers later
    arcpy.Clip_management("nibble", "", "watersheds_ras", "hu8_buffered",
                          "NoData", "ClippingGeometry")

    # Convert watershed raster to polygon.
    # Setting simplify keyword to TRUE in RasterToPolygon_conversion
    # is not working reliably so need to do this in two steps, unfortunately
    cu.multi_msg(
        "Converted reshaped watersheds raster to polygons. If you experience problems with this step, please read Known and Outstanding Bugs.txt"
    )
    arcpy.RasterToPolygon_conversion("watersheds_ras", "nibble_sheds",
                                     'SIMPLIFY', "Value")  #simplify okay

    ##    # I'm using 15 as the tolerance
    ##    # here because the diagonal of a 10x10 pixel is 14.14 m and
    ##    # I'm okay with a vertex moving as far as it can on the edges of the pixel
    ##    # This also produces results very similar to using the simplify setting
    ##    # on RasterToPolygon_conversion, when it works.
    ##    arcpy.SimplifyPolygon_cartography("nibble_sheds_unsimple",
    ##        "nibble_sheds_simplify", "POINT_REMOVE", "15 Meters", "0 SquareMeters",
    ##        "RESOLVE_ERRORS", "NO_KEEP")
    arcpy.Clip_analysis("nibble_sheds", "hu8", "final_watersheds")

    # Join Permanent ID from Waterbody seed shapefile
    final_watersheds_out = os.path.join(
        out_gdb, 'huc{}_final_watersheds'.format(huc8_code))
    arcpy.JoinField_management("final_watersheds", grid_code, seedpoly,
                               'POUR_ID', ['Permanent_Identifier'])

    # this block bumps out sheds so that they fully contain their own lakes
    # sometimes a little bit of another shed is overlapping the lake simply
    # due to the raster/polygon differences
    # 1) delete fields so watersheds and seedpoly share schema
    # 2) update features, keeping borders
    # 3) instead of lots of nulls make unique dissolve_id for all so that nulls aren't dissolved into one
    # 4) dissolve features on dissolve_id keeping the Permanent_Identifier field
    arcpy.CopyFeatures_management(seedpoly, 'lakes_nofields')
    for fc in ['lakes_nofields', 'final_watersheds']:
        fields = arcpy.ListFields(fc)
        for f in fields:
            if f != 'Permanent_Identifier':
                try:
                    arcpy.DeleteField_management(fc, f)
                except:
                    continue
    arcpy.Update_analysis("final_watersheds", 'lakes_nofields', 'update_fc')
    arcpy.AddField_management('update_fc', 'dissolve_id', 'TEXT', 255)
    arcpy.MakeFeatureLayer_management('update_fc', 'update_lyr')
    arcpy.SelectLayerByAttribute_management(
        'update_lyr', 'NEW_SELECTION',
        """"Permanent_Identifier" is not null""")
    arcpy.CalculateField_management('update_lyr', 'dissolve_id',
                                    '!Permanent_Identifier!', 'PYTHON')
    arcpy.SelectLayerByAttribute_management('update_lyr', 'SWITCH_SELECTION')
    arcpy.CalculateField_management('update_lyr', 'dissolve_id', '!OBJECTID!',
                                    'PYTHON')
    arcpy.SelectLayerByAttribute_management('update_lyr', 'CLEAR_SELECTION')
    arcpy.Dissolve_management('update_lyr', "final_watersheds_bumped",
                              'dissolve_id', 'Permanent_Identifier FIRST')
    cu.rename_field("final_watersheds_bumped",
                    "FIRST_Permanent_Identifier",
                    "Permanent_Identifier",
                    deleteOld=True)
    arcpy.DeleteField_management('final_watersheds_bumped', 'dissolve_id')

    arcpy.Clip_analysis('final_watersheds_bumped', 'hu8',
                        'final_watersheds_clipped')

    arcpy.CopyFeatures_management("final_watersheds_clipped",
                                  final_watersheds_out)

    temp_items = arcpy.ListRasters() + arcpy.ListFeatureClasses() + [temp_gdb]
    for item in temp_items:
        try:
            arcpy.Delete_management(item)
        except:
            continue

    arcpy.ResetEnvironments()
    arcpy.CheckInExtension('Spatial')
    cu.multi_msg("Complete.")
def split_strahler(stream_area_fc, streams, out_area_fc):
    """This function splits up the NHDArea feature class, which does not
    start and stop polygons at confluences, by creating break points near the
    confluences to split up the polygons. Then, it adds the Strahler value from
    the stream centerline."""
    # 1) Generate euclidean allocation raster from streams (use OBJECTID)
    # 2) Convert euclidean allocation raster to polygons
    # 3) Join allocation polygons "gridcode" to streams "OBJECTID" so that
    #    Strahler value is attached to allocation polygon
    # 4) Use identity function to split up the StreamRiver polygons at the
    #    allocation polygon boundaries, and add the Strahler values
    old_workspace = env.workspace
    env.workspace = 'in_memory'
    cu.multi_msg(
        "Splitting stream area polygons between confluences and joining 1) Strahler order to them..."
    )
    cu.multi_msg('next messages for testing')
    arcpy.CheckOutExtension('Spatial')
    cu.multi_msg('euc')
    euc = EucAllocation(streams, cell_size='50', source_field='OBJECTID')
    arcpy.CheckInExtension('Spatial')
    cu.multi_msg('conversion')
    arcpy.RasterToPolygon_conversion(euc, 'allocation_polys')
    stream_id_field = arcpy.ListFields(streams, 'Permanent_')[0].name
    cu.multi_msg('join')
    arcpy.JoinField_management('allocation_polys', 'grid_code', streams,
                               'OBJECTID',
                               ['Strahler', 'LengthKm', stream_id_field])
    cu.multi_msg('identity')
    arcpy.Identity_analysis(stream_area_fc, 'allocation_polys', out_area_fc)
    env.workspace = old_workspace
    cu.multi_msg("Splitting strema area polygons finished.")
def wetland_order(rivex, stream_area_fc, nwi, out_fc):
    arcpy.env.workspace = 'in_memory'
    arcpy.env.outputCoordinateSystem = arcpy.SpatialReference(102039)
    arcpy.env.extent = nwi

    # Buffer the wetland perimeters by 30 meters
    cu.multi_msg('Creating 30m wetland buffers...')
    arcpy.Buffer_analysis(nwi, "wetland_buffers", "30 meters", "OUTSIDE_ONLY")
    arcpy.env.extent = "wetland_buffers"

    cu.multi_msg('Preparing for river line and area merge...')
    arcpy.CopyFeatures_management(rivex, 'rivex_extent')
    arcpy.CopyFeatures_management(stream_area_fc, 'stream_area_extent')
    arcpy.MakeFeatureLayer_management('rivex_extent', 'rivex_lyr')
    arcpy.SelectLayerByLocation_management('rivex_lyr', 'COMPLETELY_WITHIN', stream_area_fc)
    arcpy.CopyFeatures_management('rivex_lyr', 'rivex_for_splitting')
    arcpy.SelectLayerByAttribute_management('rivex_lyr', 'SWITCH_SELECTION')
    arcpy.CopyFeatures_management('rivex_lyr', 'rivex_not_areas')
    split_strahler('stream_area_extent', 'rivex_for_splitting', 'stream_area_split')

    # areas TO lines
    arcpy.PolygonToLine_management('stream_area_split', 'streamarea_to_line', False)

    # Merge features together
    arcpy.Merge_management(['streamarea_to_line', 'rivex_not_areas'], 'merged_rivers', 'NO_TEST')

    # FOR THE LINE-BASED PORTION
    # Spatial join connected wetlands and streams
    ##################Field Maps########################
    fms = arcpy.FieldMappings()
    fm_strahlermax = arcpy.FieldMap()
    fm_strahlersum = arcpy.FieldMap()
    fm_lengthkm = arcpy.FieldMap()
    fm_wetid = arcpy.FieldMap()

    fm_strahlermax.addInputField('merged_rivers', "Strahler")
    fm_strahlersum.addInputField('merged_rivers', "Strahler")
    fm_lengthkm.addInputField('merged_rivers', "LengthKm")
    fm_wetid.addInputField("wetland_buffers", "WET_ID")

    fm_lengthkm.mergeRule = 'Sum'
    fm_strahlermax.mergeRule = 'Max'
    fm_strahlersum.mergeRule = 'Sum'

    lengthkm_name = fm_lengthkm.outputField
    lengthkm_name.name = 'StreamKm'
    lengthkm_name.aliasName = 'StreamKm'
    fm_lengthkm.outputField = lengthkm_name

    strahlermax_name = fm_strahlermax.outputField
    strahlermax_name.name = 'StrOrdMax'
    strahlermax_name.aliasName = 'StrOrdMax'
    fm_strahlermax.outputField = strahlermax_name

    strahlersum_name = fm_strahlersum.outputField
    strahlersum_name.name = 'StrOrdSum'
    strahlersum_name.aliasName = 'StrOrdSum'
    fm_strahlersum.outputField = strahlersum_name

    fms.addFieldMap(fm_strahlermax)
    fms.addFieldMap(fm_strahlersum)
    fms.addFieldMap(fm_lengthkm)
    fms.addFieldMap(fm_wetid)
    #####################################################

    arcpy.SpatialJoin_analysis("wetland_buffers", 'merged_rivers', "wetland_spjoin_streams", '', '', fms)

    # Get the stream count from the join count
    cu.rename_field("wetland_spjoin_streams", 'Join_Count', "StreamCnt", True)

    # Join the new fields back to the original feature class based on WET_ID
    join_fields = ['StrOrdMax', 'StrOrdSum', 'StreamKm', 'StreamCnt']
    arcpy.CopyFeatures_management(nwi, out_fc)
    arcpy.JoinField_management(out_fc, 'WET_ID', 'wetland_spjoin_streams', 'WET_ID', join_fields)

    # Set these to 0 where there is no connection
    cu.redefine_nulls(out_fc, join_fields, [0,0,0,0])

    # Classify VegType: 4 options based on class code in ATTRIBUTE field
    arcpy.AddField_management(out_fc, "VegType", "TEXT")
    with arcpy.da.UpdateCursor(out_fc, ["ATTRIBUTE", "VegType"]) as cursor:
        for row in cursor:
            attr_abbv = row[0][:3]
            if attr_abbv == "PEM" or attr_abbv == "PAB":
                row[1] = "PEMorPAB"
            elif attr_abbv == "PFO":
                row[1] = "PFO"
            elif attr_abbv == "PSS":
                row[1] = "PSS"
            else:
                row[1] = "Other"
            cursor.updateRow(row)

    # Determine the regime from the letter code. Examples: PSS1E ---> E,
    #  PEM1/SS1Fb --> F
    class_codes = 'RB UB AB US ML EM SS FO'.split()
    regime_codes = 'A B C E F G H J K'.split()
    arcpy.AddField_management(out_fc, "Regime", "TEXT")
    with arcpy.da.UpdateCursor(out_fc, ["ATTRIBUTE", "Regime"]) as cursor:
        for row in cursor:
            # All the wetlands are already palustrine, so if we remove the class
            # codes, any capital letters left besides the P in front
            # are the regime code
            # example codes: PEM1E, PSS1/EM1E, PEM1/5C, PUSA, PSSf
            # If you ever can figure out the regex for this instead, go ahead.
            code_value = row[0]
            regime_value = 'unknown'
            # this bit searches for the class codes and replaces them with nothing
            # this is necessary because meaning of A, B, E, F is context dependent
            for class_code in class_codes:
                if class_code in code_value:
                    code_value = code_value.replace(class_code, '')
            for char in code_value:
                if char in regime_codes:
                    regime_value = char
            row[1] = regime_value
            cursor.updateRow(row)

    # Calculate WetOrder from StrOrdSum
    arcpy.AddField_management(out_fc,"WetOrder", "TEXT")
    with arcpy.da.UpdateCursor(out_fc, ["StrOrdSum", "WetOrder"]) as cursor:
        for row in cursor:
            if row[0] == 0:
                row[1] = "Isolated"
            elif row[0] == 1:
                row[1] = "Single"
            elif row[0] == None:
                row[1] = "Isolated"
            else:
                row[1] = "Connected"
            cursor.updateRow(row)
    arcpy.Delete_management('in_memory')
def upstream_lakes(nhd_gdb, output_table):
    #define paths in nhd geodatabase
    nhd_waterbody = os.path.join(nhd_gdb, 'NHDWaterbody')
    nhd_flowline = os.path.join(nhd_gdb, 'NHDFlowline')
    nhd_junctions = os.path.join(nhd_gdb, 'HYDRO_NET_Junctions')
    hydro_net = os.path.join(nhd_gdb, 'Hydrography', 'HYDRO_NET')

    arcpy.env.workspace = 'in_memory'
    arcpy.env.outputCoordinateSystem = arcpy.SpatialReference(102039)

    arcpy.MakeFeatureLayer_management(nhd_junctions, 'junctions')

    cu.multi_msg('Preparing layers and fields for calculations....')

    # select only lakes as defined in our project: waterbodies with one of these
    # types and greater than 4ha, and certain reservoirs greater than 10ha
    fcodes = (39000, 39004, 39009, 39010, 39011, 39012, 43600, 43613, 43615, 43617, 43618, 43619, 43621)
    gte_4ha_lakes_query = '''("AreaSqKm" >=0.04 AND "FCode" IN %s) OR ("FCode" = 43601 AND "AreaSqKm" >= 0.1)''' % (fcodes,)
    gte_10ha_lakes_query = '''("AreaSqKm" >=0.1 AND "FCode" IN %s) OR ("FCode" = 43601 AND "AreaSqKm" >= 0.1)''' % (fcodes,)

    arcpy.MakeFeatureLayer_management(nhd_waterbody, 'gte_4ha_lakes', gte_4ha_lakes_query)
    arcpy.MakeFeatureLayer_management(nhd_waterbody, 'gte_10ha_lakes', gte_10ha_lakes_query)
    arcpy.CopyRows_management('gte_4ha_lakes', 'output_table')

    # (need this for line 61 make feature layer to work right!)
    arcpy.CopyFeatures_management('gte_4ha_lakes', 'gte_4ha_lakes_DISK')

    # add the new fields we'll calculate: count and area
    count_fields = ['Upstream_Lakes_4ha_Count', 'Upstream_Lakes_10ha_Count']
    area_fields = ['Upstream_Lakes_4ha_Area_ha', 'Upstream_Lakes_10ha_Area_ha']
    for cf in count_fields:
        arcpy.AddField_management('output_table', cf, 'LONG')
    for af in area_fields:
        arcpy.AddField_management('output_table', af, 'DOUBLE')
    new_fields= count_fields + area_fields

    # for each lake, use its junctions as input flags to the upstream trace, then
    # evalute the traced network for how many lakes are in it
    cu.multi_msg("Calculating upstream network for each lake. This may take a few minutes, or a few hours...")
    with arcpy.da.UpdateCursor('output_table', ['Permanent_Identifier'] + new_fields) as cursor:
        for row in cursor:
            id = row[0]

            # get the junction points on top of this lake, can be 0 or several
            where_clause = """"{0}" = '{1}'""".format('Permanent_Identifier', id)
            arcpy.MakeFeatureLayer_management('gte_4ha_lakes_DISK', "this_lake",
                                                where_clause)
            arcpy.SelectLayerByLocation_management('junctions', "INTERSECT",
                                            "this_lake", "1 Meters")
            count_jxns = int(arcpy.GetCount_management('junctions').getOutput(0))

            # if the lake has no upstream connectivity whatsoever, it will have
            # no junctions on top of it. assign 0s
            # to the counts of upstream lakes. This can either be an "isolated"
            # lake or a "headwater" lake
            if count_jxns <= 0:
                row[1:] = [0, 0, 0, 0]

            # if there are any junctions, go ahead and trace upstream
            else:
                arcpy.CopyFeatures_management("junctions", 'this_lake_jxns')
                arcpy.TraceGeometricNetwork_management(hydro_net, "upstream",
                                    'this_lake_jxns', "TRACE_UPSTREAM")
                arcpy.SelectLayerByLocation_management('gte_4ha_lakes', "INTERSECT",
                                    "upstream/NHDFlowline", '1 Meters', 'NEW_SELECTION')
                # Remove this lake from the selection!!
                arcpy.SelectLayerByAttribute_management('gte_4ha_lakes',
                                    'REMOVE_FROM_SELECTION', where_clause)
                count_4ha_lakes = int(arcpy.GetCount_management('gte_4ha_lakes').getOutput(0))

                # if there is upstream connectivity but no countable lakes
                # assign 0s for all the counts/areas and do not try to copy rows
                if count_4ha_lakes <= 0:
                    row[1:] = [0, 0, 0, 0]

                # if there are any lakes upstream, copy rows before doing
                # counts so we can use a search cursor on just these lakes
                else:
                    arcpy.CopyRows_management('gte_4ha_lakes', 'these_4ha_lakes')

                    # row[1] is 4ha_Upstream_Lakes_Count
                    # row [3] is 4ha_Upstream_Lakes_Area
                    row[1] = int(arcpy.GetCount_management('these_4ha_lakes').getOutput(0))
                    total_area = 0
                    with arcpy.da.SearchCursor('these_4ha_lakes', ['AreaSqKm']) as area4_cursor:
                        for area4_row in area4_cursor:
                            total_area += area4_row[0] * 100
                    row[3] = total_area

                    # same but for 10ha
                    arcpy.SelectLayerByLocation_management('gte_10ha_lakes', "INTERSECT",
                                        "upstream/NHDFlowline", '1 Meters', 'NEW_SELECTION')
                    arcpy.SelectLayerByAttribute_management('gte_10ha_lakes',
                                    'REMOVE_FROM_SELECTION', where_clause)
                    count_10ha_lakes = int(arcpy.GetCount_management('gte_10ha_lakes').getOutput(0))

                    # don't try to copy rows if selection is empty
                    if count_10ha_lakes <= 0:
                        row[2] = 0
                        row[4] = 0
                    # if there are features selected, copy rows so we
                    # can use a search cursor
                    else:
                        arcpy.CopyRows_management('gte_10ha_lakes', 'these_10ha_lakes')
                        row[2] = int(arcpy.GetCount_management('these_10ha_lakes').getOutput(0))
                        total_area = 0
                        with arcpy.da.SearchCursor('these_4ha_lakes', ['AreaSqKm']) as area10_cursor:
                            for area10_row in area10_cursor:
                                total_area += area10_row[0] * 100
                        row[4] = total_area
            cursor.updateRow(row)

            # delete intermediates before next iteration
            for item in ['this_lake', 'this_lake_jxns', 'upstream', 'these_4ha_lakes', 'these_10ha_lakes']:
                    try:
                        arcpy.Delete_management(item)
                    except:
                        continue

    # clean up the output table
    all_fields = [f.name for f in arcpy.ListFields('output_table')]
    for f in all_fields:
        if f not in ['Permanent_Identifier'] + new_fields:
            try:
                arcpy.DeleteField_management('output_table', f)
            except:
                continue

    # write out the final file and clean up intermediates
    arcpy.CopyRows_management('output_table', output_table)
    for item in ['junctions', 'gte_4ha_lakes', 'gte_10ha_lakes', 'output_table']:
        arcpy.Delete_management(item)
def lakes_in_zones(zones_fc, zone_field, lakes_fc, output_table):

    # make sure we're only using the right types of lakes, our feature
    # class excludes everything else but this is a guarantee this will
    # get checked at some point
    arcpy.env.workspace = 'in_memory'

    # using in_memory workspace means no SHAPE@AREA attribute later so I
    # need to calculate another field with the area using temp on disk
    # then go ahead and copy to memory, delete temp
    temp_workspace = cu.create_temp_GDB('lakezone')
    temp_lakes = os.path.join(temp_workspace, 'temp_lakes')
    arcpy.CopyFeatures_management(lakes_fc, temp_lakes)
    arcpy.AddField_management(temp_lakes, 'Area_ha', 'DOUBLE')
    arcpy.CalculateField_management(temp_lakes, 'Area_ha',
                                    '!shape.area@hectares!', 'PYTHON')

    # this bit enforces the correct lake type/size restriction just in case
    # geodata doesn't have this filtered already
    need_selection = False
    fcodes = (39000, 39004, 39009, 39010, 39011, 39012, 43600, 43613, 43615,
              43617, 43618, 43619, 43621)
    with arcpy.da.SearchCursor(temp_lakes, ["FCode"]) as cursor:
        for row in cursor:
            if row[0] not in fcodes + (43601, ):
                need_selection = True

    if need_selection:
        whereClause = '''
                    ("AreaSqKm" >=0.04 AND "FCode" IN %s)\
                    OR ("AreaSqKm" >= 0.1 AND "FCode" = 43601)''' % (fcodes, )
        arcpy.Select_analysis(temp_lakes, "lakes_4ha", whereClause)
        temp_lakes = os.path.join(arcpy.env.workspace, "lakes_4ha")

    selections = [
        """"Area_ha" >= 4""",
        """"Area_ha" >= 4 AND "Connection" = 'Isolated'""",
        """"Area_ha" >= 4 AND "Connection" = 'Headwater'""",
        """"Area_ha" >= 4 AND "Connection" = 'DR_Stream'""",
        """"Area_ha" >= 4 AND "Connection" = 'DR_LakeStream'""",
        """"Area_ha" >= 4 AND "Area_ha" < 10""",
        """"Area_ha" >= 4 AND "Area_ha" < 10 AND "Connection" = 'Isolated'""",
        """"Area_ha" >= 4 AND "Area_ha" < 10 AND "Connection" = 'Headwater'""",
        """"Area_ha" >= 4 AND "Area_ha" < 10 AND "Connection" = 'DR_Stream'""",
        """"Area_ha" >= 4 AND "Area_ha" < 10 AND "Connection" = 'DR_LakeStream'""",
        """"Area_ha" >= 10""",
        """"Area_ha" >= 10 AND "Connection" = 'Isolated'""",
        """"Area_ha" >= 10 AND "Connection" = 'Headwater'""",
        """"Area_ha" >= 10 AND "Connection" = 'DR_Stream'""",
        """"Area_ha" >= 10 AND "Connection" = 'DR_LakeStream'"""
    ]
    temp_tables = [
        'Lakes4ha', 'Lakes4ha_Isolated', 'Lakes4ha_Headwater',
        'Lakes4ha_DRStream', 'Lakes4ha_DRLakeStream', 'Lakes4to10ha',
        'Lakes4to10ha_Isolated', 'Lakes4to10ha_Headwater',
        'Lakes4to10ha_DRStream', 'Lakes4to10ha_DRLakeStream', 'Lakes10ha',
        'Lakes10ha_Isolated', 'Lakes10ha_Headwater', 'Lakes10ha_DRStream',
        'Lakes10ha_DRLakeStream'
    ]

    for sel, temp_table in zip(selections, temp_tables):
        cu.multi_msg(
            "Creating temporary table called {0} for lakes where {1}".format(
                temp_table, sel))
        polygons_in_zones.polygons_in_zones(zones_fc, zone_field, temp_lakes,
                                            temp_table, sel)
        new_fields = [
            'Poly_Overlapping_AREA_ha', 'Poly_Contributing_AREA_ha',
            'Poly_Overlapping_AREA_pct', 'Poly_Count'
        ]
        avg_size_field = temp_table + '_AvgSize_ha'
        arcpy.AddField_management(temp_table, avg_size_field, 'DOUBLE')
        arcpy.CalculateField_management(
            temp_table, avg_size_field,
            '!Poly_Contributing_AREA_ha!/!Poly_Count!', 'PYTHON')
        for f in new_fields:
            cu.rename_field(temp_table, f, f.replace('Poly', temp_table), True)

    # join em up and copy to final
    temp_tables.remove('Lakes4ha')
    for t in temp_tables:
        try:
            arcpy.JoinField_management('Lakes4ha', zone_field, t, zone_field)
        #sometimes there's no table if it was an empty selection
        except:
            empty_fields = [f.replace('Poly', t) for f in new_fields]
            for ef in empty_fields:
                arcpy.AddField_management('Lakes4ha', ef, 'Double')
                arcpy.CalculateField_management('Lakes4ha', ef, '0', 'PYTHON')
            continue

    # remove all the extra zoneID fields, which have underscore in name
    drop_fields = [f.name for f in arcpy.ListFields('Lakes4ha', 'ZoneID_*')]
    for f in drop_fields:
        arcpy.DeleteField_management('Lakes4ha', f)
    arcpy.CopyRows_management('Lakes4ha', output_table)

    # clean up
    for item in ['Lakes4ha', temp_lakes,
                 os.path.dirname(temp_workspace)] + temp_tables:
        arcpy.Delete_management(item)
def percent_geographic_doubles(polygon_fc,
                               unique_id,
                               percent_overlap_allowed=10,
                               keep_fc='',
                               keep_field=''):
    neighbor_table = 'in_memory/neighbortable'
    cu.multi_msg('Calculating neighbor table...')
    arcpy.PolygonNeighbors_analysis(polygon_fc, neighbor_table, unique_id,
                                    'AREA_OVERLAP', 'NO_BOTH_SIDES')

    # need these to avoid some naming problems that arise from trying to
    # do this directly
    src_field = arcpy.ListFields(neighbor_table, 'src*')[0].name
    nbr_field = arcpy.ListFields(neighbor_table, 'nbr*')[0].name

    arcpy.CopyFeatures_management(polygon_fc, 'in_memory/fc')
    fdate_field = arcpy.ListFields('in_memory/fc', '*FDate*')[0].name
    cu.multi_msg('Joining neighbor table to feature class...')
    arcpy.JoinField_management('in_memory/fc', unique_id, neighbor_table,
                               src_field)

    cursor_fields = ['AREA', 'SHAPE@AREA', unique_id, nbr_field, fdate_field]
    ##    print([f.name for f in arcpy.ListFields('in_memory/fc')])

    if keep_fc:
        keep_ids = [
            row[0] for row in arcpy.da.SearchCursor(keep_fc, keep_field)
        ]
    else:
        keep_ids = []

    with arcpy.da.SearchCursor('in_memory/fc', cursor_fields) as cursor:
        for row in cursor:

            # If this row represents a duplicate-type overlap
            if row[0] >= row[1] * (100 - percent_overlap_allowed) / 100:
                src_value = row[2]
                nbr_value = row[3]
                cu.multi_msg("testing. pair: %s and %s" %
                             (src_value, nbr_value))

                # Then lookup both rows in the overlap and delete the one with
                # the oldest FDate
                where_clause = '''"%s" = '%s' OR "%s" = '%s' ''' % (
                    unique_id, src_value, unique_id, nbr_value)
                dates = [
                    row[4] for row in arcpy.da.SearchCursor(
                        'in_memory/fc', cursor_fields, where_clause)
                ]
                cu.multi_msg("testing. dates %s and %s not in order" %
                             (dates[0], dates[1]))
                with arcpy.da.UpdateCursor('in_memory/fc', cursor_fields,
                                           where_clause) as c:
                    for r in c:
                        if r[4] == min(dates) and r[4] == max(dates):
                            cu.multi_msg(
                                "PROBLEM! Same date. Resolve pair %s, %s manually."
                                % (src_value, nbr_value))
                        if r[4] == min(dates) and r[4] != max(dates):
                            if r[4] not in keep_ids:
                                cu.multi_msg(
                                    "%s has the older date (%s) and would be removed."
                                    % (r[2], r[4]))

                            if r[4] in keep_ids:
                                cu.multi_msg(
                                    "You're going to have to write this condition..."
                                )
                        else:
                            continue
            else:
                continue

    for item in [neighbor_table, 'in_memory/fc']:
        arcpy.Delete_management(item)
def create_csi_watersheds(flowdir, pour_dir, nhd_gdb, out_gdb):

    # Starting environmental variables:
    env.extent = flowdir
    env.snapRaster = flowdir
    env.cellSize = 10
    env.outputCoordinateSystem = arcpy.SpatialReference(102039)
    arcpy.CheckOutExtension('Spatial')

    huc8_code = re.search('\d{8}', os.path.basename(flowdir)).group()
    huc4_code = re.search('\d{4}', os.path.basename(nhd_gdb)).group()

    # create temp directory because we need shape geometry
    temp_gdb = cu.create_temp_GDB('watersheds' + huc4_code)
    print temp_gdb
    env.workspace = temp_gdb

    wbd_hu8 = os.path.join(nhd_gdb, "WBD_HU8")
    field_name = (arcpy.ListFields(wbd_hu8, "HU*8"))[0].name
    whereClause8 =  """{0} = '{1}'""".format(arcpy.AddFieldDelimiters(nhd_gdb, field_name), huc8_code)
    arcpy.Select_analysis(wbd_hu8, "hu8", whereClause8)
    arcpy.Buffer_analysis("hu8", "hu8_buffered", "100 meters")


    # Create the basic watersheds
    pour_points = os.path.join(pour_dir, 'pour_points.tif')
    arcpy.Clip_management(pour_points, '', "pour_points_clipped", "hu8_buffered", '0', 'ClippingGeometry')
    raw_watersheds = os.path.join(out_gdb, 'huc{}_watersheds_precursors'.format(huc8_code))
    cu.multi_msg("Calculating preliminary watersheds...")
    outWatershed = Watershed(flowdir, "pour_points_clipped")
    outWatershed.save(raw_watersheds)


    cu.multi_msg("Clipping watersheds to subregion boundaries and filtering spurious watersheds...")

    # Watershed raster to polygons
    arcpy.RasterToPolygon_conversion(raw_watersheds, "wspoly1", 'NO_SIMPLIFY', "Value")

    # Clip watershed polygons to subregion polys.
    arcpy.Clip_analysis("wspoly1", "hu8", "wsclip1")

    # Clip watershed

##    # Calculate hectares
##    arcpy.AddField_management("wsclip1", "HA", "DOUBLE")
##    arcpy.CalculateField_management("wsclip1", "HA", '''!shape.area@hectares!''', "PYTHON")

    # Create fc of watershed polygons >= 1 ha that coincide with seed lines and polys.
    seedline = os.path.join(pour_dir, 'pourpoints.gdb', 'eligible_flowlines')
    seedpoly = os.path.join(pour_dir, 'pourpoints.gdb', 'eligible_lakes')
    arcpy.MakeFeatureLayer_management("wsclip1", "wsclip1_lyr")

    arcpy.SelectLayerByLocation_management("wsclip1_lyr", "INTERSECT", seedline, '', "NEW_SELECTION")
    arcpy.SelectLayerByLocation_management("wsclip1_lyr", "INTERSECT", seedpoly, '', "ADD_TO_SELECTION")
    arcpy.SelectLayerByAttribute_management("wsclip1_lyr", "SUBSET_SELECTION",'''"Shape_Area" >= 10000''')


    cu.multi_msg("Reshaping watersheds...")
    # Polygon back to raster
    arcpy.PolygonToRaster_conversion("wsclip1_lyr", "grid_code", "ws_legit_ras")
    arcpy.Clip_management("ws_legit_ras", '', "ws_legit_clipped_ras",
                          "hu8", "0", "ClippingGeometry")

    # Make a raster from the subregion (boundary) polygon with zero for cell values.
    arcpy.AddField_management("hu8", "Value", "SHORT")
    arcpy.CalculateField_management("hu8", "Value", "0", "PYTHON")
    arcpy.PolygonToRaster_conversion("hu8", "Value","boundary_raster")
    arcpy.Clip_management("boundary_raster", '', "boundary_raster_clip", "hu8", '', "ClippingGeometry")

    # Fill NoData in watersheds with the zero values from the subregion raster's cells.
    composite = Con(IsNull("ws_legit_clipped_ras"), "boundary_raster_clip", "ws_legit_clipped_ras")
    composite.save("composite_raster")
    arcpy.Clip_management("composite_raster", '', "composite_raster_clip", "hu8", '0', "ClippingGeometry")

    # Make a mask of zero cells. NoData cells are the actual mask for nibble.
    premask = Con(IsNull("composite_raster_clip"), "composite_raster_clip", 0)
    premask.save("premask")

    arcpy.Clip_management("premask",'', "mask", "hu8", '', "ClippingGeometry")

    # Set Null to 1.
    pre_watersheds = Con(IsNull("composite_raster_clip"), 1, "composite_raster_clip")
    pre_watersheds.save("pre_watersheds") # does this speed things up?
##    prews.save("prews.tif")

    # Nibble masked values (null values along boundary).
    cu.multi_msg('Nibbling watersheds as part of reshaping...')
    nibble = Nibble("pre_watersheds", "mask", "DATA_ONLY")
    nibble.save("nibble")
    # Use HU8 buffer so that watersheds will overrun HU8 boundaries and get
    # clipped without weird slivers later
    arcpy.Clip_management("nibble", "", "watersheds_ras", "hu8_buffered", "NoData","ClippingGeometry")

    # Convert watershed raster to polygon.
    # Setting simplify keyword to TRUE in RasterToPolygon_conversion
    # is not working reliably so need to do this in two steps, unfortunately
    cu.multi_msg("Converted reshaped watersheds raster to polygons. If you experience problems with this step, please read Known and Outstanding Bugs.txt")
    arcpy.RasterToPolygon_conversion("watersheds_ras", "nibble_sheds",'SIMPLIFY', "Value") #simplify okay

##    # I'm using 15 as the tolerance
##    # here because the diagonal of a 10x10 pixel is 14.14 m and
##    # I'm okay with a vertex moving as far as it can on the edges of the pixel
##    # This also produces results very similar to using the simplify setting
##    # on RasterToPolygon_conversion, when it works.
##    arcpy.SimplifyPolygon_cartography("nibble_sheds_unsimple",
##        "nibble_sheds_simplify", "POINT_REMOVE", "15 Meters", "0 SquareMeters",
##        "RESOLVE_ERRORS", "NO_KEEP")
    arcpy.Clip_analysis("nibble_sheds", "hu8", "final_watersheds")

    # Join Permanent ID from Waterbody seed shapefile
    final_watersheds_out = os.path.join(out_gdb, 'huc{}_final_watersheds'.format(huc8_code))
    arcpy.JoinField_management("final_watersheds", 'grid_code', seedpoly, 'POUR_ID', ['Permanent_Identifier'])

    # this block bumps out sheds so that they fully contain their own lakes
    # sometimes a little bit of another shed is overlapping the lake simply
    # due to the raster/polygon differences
    # 1) delete fields so watersheds and seedpoly share schema
    # 2) update features, keeping borders
    # 3) instead of lots of nulls make unique dissolve_id for all so that nulls aren't dissolved into one
    # 4) dissolve features on dissolve_id keeping the Permanent_Identifier field
    arcpy.CopyFeatures_management(seedpoly, 'lakes_nofields')
    for fc in ['lakes_nofields', 'final_watersheds']:
        fields = arcpy.ListFields(fc)
        for f in fields:
            if f != 'Permanent_Identifier':
                try:
                    arcpy.DeleteField_management(fc, f)
                except:
                    continue
    arcpy.Update_analysis("final_watersheds", 'lakes_nofields', 'update_fc')
    arcpy.AddField_management('update_fc', 'dissolve_id', 'TEXT', 255)
    arcpy.MakeFeatureLayer_management('update_fc', 'update_lyr')
    arcpy.SelectLayerByAttribute_management('update_lyr', 'NEW_SELECTION', """"Permanent_Identifier" is not null""")
    arcpy.CalculateField_management('update_lyr', 'dissolve_id', '!Permanent_Identifier!', 'PYTHON')
    arcpy.SelectLayerByAttribute_management('update_lyr', 'SWITCH_SELECTION')
    arcpy.CalculateField_management('update_lyr', 'dissolve_id', '!OBJECTID!', 'PYTHON')
    arcpy.SelectLayerByAttribute_management('update_lyr', 'CLEAR_SELECTION')
    arcpy.Dissolve_management('update_lyr', "final_watersheds_bumped", 'dissolve_id', 'Permanent_Identifier FIRST')
    cu.rename_field("final_watersheds_bumped", "FIRST_Permanent_Identifier", "Permanent_Identifier", deleteOld = True)
    arcpy.DeleteField_management('final_watersheds_bumped', 'dissolve_id')

    arcpy.Clip_analysis('final_watersheds_bumped', 'hu8', 'final_watersheds_clipped')

    arcpy.CopyFeatures_management("final_watersheds_clipped", final_watersheds_out)

    temp_items = arcpy.ListRasters() + arcpy.ListFeatureClasses() + [temp_gdb]
    for item in temp_items:
        try:
            arcpy.Delete_management(item)
        except:
            continue

    arcpy.ResetEnvironments()
    arcpy.CheckInExtension('Spatial')
    cu.multi_msg("Complete.")
def stage_files(nhd_gdb, ned_dir, ned_footprints_fc, out_dir, is_zipped):
    env.workspace = 'in_memory'

    #####################################################################
    cu.multi_msg("1) Creating Directory Structure and Copying NHD Geodatabase")
    #####################################################################
    #finds the 4-digit huc code in the filename
    huc4_code = re.search('\d{4}', os.path.basename(nhd_gdb)).group()

    out_subdir = os.path.join(out_dir, "NHD" + huc4_code)
    if not os.path.exists(out_subdir):
        os.mkdir(out_subdir)
    nhd_gdb_copy  = os.path.join(out_subdir, os.path.basename(nhd_gdb))
    arcpy.Copy_management(nhd_gdb,nhd_gdb_copy)

    ####################################################################
    cu.multi_msg("2) Getting WBD Poly For Subregion and Buffering by 5000m...")
    #####################################################################

    #select only this subregion from the wbd layer in the nhd_gdb (bordering
    # subregions are included in there too) and buffer it
    wbd_hu4 = os.path.join(nhd_gdb_copy, "WBD_HU4")
    field_name = (arcpy.ListFields(wbd_hu4, "HU*4"))[0].name
    whereClause =  """{0} = '{1}'""".format(arcpy.AddFieldDelimiters(nhd_gdb_copy, field_name), huc4_code)
    arcpy.MakeFeatureLayer_management(wbd_hu4, "wbd_poly", whereClause)
    arcpy.Buffer_analysis("wbd_poly", "wbd_buf", "5000 meters")

    #####################################################################
    cu.multi_msg("3) Clipping NED Tile polys from Buffered NHD Subregion From WBD...")
    #####################################################################
    arcpy.Clip_analysis(ned_footprints_fc, "wbd_buf", "ned_clip")

    #####################################################################
    cu.multi_msg("4) Getting File_ID of clipped NED footprint polys and copying NED data to output location")
    #####################################################################
    missing_NED_list = []
    with arcpy.da.SearchCursor("ned_clip", ["FILE_ID"]) as cursor:
        for row in cursor:
            file_id = row[0].replace("g","")

            # unzipping if needed

            if is_zipped:
                unzipped_file = unzip_ned(file_id, ned_dir, out_subdir)
                if not unzipped_file:
                    missing_NED_list.append(file_id)
            else:

                # copy ned tiles to output location
                ned_source = os.path.join(ned_dir, file_id)
                ned_destination = os.path.join(out_subdir,  file_id)
                if not os.path.exists(ned_source):
                    cu.multi_msg("ERROR: Tile %s does not exist in the specified location" % file_id)
                    missing_NED_list.append(file_id)
                else:
                    if not os.path.exists(ned_destination):
                        shutil.copytree(ned_source, ned_destination)
                    else:
                        cu.multi_msg("Output folder for this NED tile already exists.")
    if missing_NED_list:
        warning_text = "WARNING: NED tiles did not exist for the following: %s" % ','.join(missing_NED_list)
        arcpy.AddWarning(warning_text)
        print(warning_text)
    for item in ["wbd_buf", "ned_clip"]:
        arcpy.Delete_management(item)
    return out_subdir
def remove_nhd_duplicates(in_fc, unique_id, out_fc):
    """This function will resolve identifier duplications from any NHD-based
    layer. Use when you need to be sure the identifier values are unique. Some
    processing chains will result in duplications where edits to the feature
    are apparently saved twice. This function will save the feature with the
    newest time stamp and drop all the rest of the records with the same value
    in the field you specify as unique_id. If there are no duplications, the
    function will exit safely.

    in_fc: the feature class that might have duplicates that is derived from an
    NHD (National Hydrological Dataset) layer such as NHDWaterbodies or
    NHDFlowlines

    unique_id: The field you want to use as a primary key, or unique identifier
    of features. Any value appearing more than once in this input will be
    resolved in the output so that it only appears once. With the NHD, this will
    usually be the 'Permanent_Identifier' column, which you may have already
    renamed.
    be the 'Permanent_Identifier' column

    out_fc: the desired path for the output feature class"""
    ws = 'in_memory'
    env.workspace = ws

    cu.multi_msg("Creating frequency table...")
    arcpy.Frequency_analysis(in_fc, "freqtable", unique_id)
    arcpy.TableSelect_analysis("freqtable", "dupeslist", '''"FREQUENCY" > 1''')

    # Make a list of truly unique ids for use in selecting groups of identical ids
    select_ids = [
        row[0] for row in arcpy.da.SearchCursor("dupeslist", (unique_id))
    ]

    # Pick out just the records we need to work with, the rest will be
    # saved to the output and we will merge in the results from our working
    # set later

    # cu.multi_msg("test1")
    # arcpy.MakeFeatureLayer_management(in_fc, "fc_lyr")
    #
    # cu.multi_msg("test2")
    # arcpy.AddJoin_management("fc_lyr", unique_id, "dupeslist", unique_id, "KEEP_COMMON")
    # arcpy.SelectLayerByAttribute_management('fc_lyr', 'NEW_SELECTION', '''"FREQUENCY" = 1''')
    # arcpy.RemoveJoin_management("fc_lyr")
    # arcpy.CopyFeatures_management("fc_lyr", "unique")
    #
    # cu.multi_msg("test3")
    # arcpy.AddJoin_management("fc_lyr", unique_id, "freqtable", unique_id)
    # arcpy.SelectLayerByAttribute_management('fc_lyr', 'NEW_SELECTION', '''"FREQUENCY" > 1''')
    # arcpy.RemoveJoin_management("fc_lyr")
    # arcpy.CopyFeatures_management("fc_lyr", "fc_temp")
    arcpy.CopyFeatures_management(in_fc, "fc_temp")
    #
    # cu.multi_msg("test4")
    arcpy.AddField_management("fc_temp", "NewestFDate", "SHORT")
    arcpy.CalculateField_management("fc_temp", "NewestFDate", 1, 'PYTHON')
    fdate_field = arcpy.ListFields('fc_temp', '*FDate', 'Date')[0].name
    unique_id = arcpy.ListFields('fc_temp', '*Permanent_Identifier',
                                 'Text')[0].name
    #
    # cu.multi_msg("test5")
    # arcpy.TableSelect_analysis("freqtable", "dupetable", '''"FREQUENCY" > 1''')

    count_dupes = int(arcpy.GetCount_management("dupeslist").getOutput(0))

    if count_dupes > 0:
        cu.multi_msg("Number of records in duplicates table is %d" %
                     count_dupes)

        # # Make a list of truly unique ids for use in selecting groups of identical ids
        # select_ids = [row[0] for row in arcpy.da.SearchCursor("dupetable", (unique_id))]

        # for each id select its group of duplicates
        for s_id in select_ids:

            # fdate_field = arcpy.ListFields('fc_temp', '*FDate', 'Date')[0].name
            # unique_id = arcpy.ListFields('fc_temp', '*Permanent_Identifier', 'Text')[0].name
            whereClause = ''' "%s" = '%s' ''' % (unique_id, s_id)

            # update values to 0, we will change only one to 1 later
            # and get a list of all the dates
            dates = [
                row[0]
                for row in arcpy.da.SearchCursor("fc_temp", (
                    fdate_field), whereClause)
            ]

            print("ID group %s" % s_id)
            # print("Date object values: %s" % dates)
            # print("Newest date: %s\n" % max(dates))

            # just using "Fdate" = max(dates) logic DOES NOT WORK CORRECTLY
            # sometimes more than one record with max date but
            # the following allows us to use "NewestFDate" = 1 later to
            # select ONLY ONE to keep
            # Update 2017-06-22: If you just run Delete Identical on full identicals, you actually could use that
            # but I'm not going to change this now.
            with arcpy.da.UpdateCursor("fc_temp", (fdate_field, "NewestFDate"),
                                       whereClause) as cursor:
                i = 1
                for row in cursor:
                    if row[0] == max(dates):
                        row[1] = i
                        i += 1
                    else:
                        row[1] = 0
                    cursor.updateRow(row)

        # create a new, distinct output rather than updating table in place
        # arcpy.Select_analysis("fc_temp", "newest_only", ''' "NewestFDate" = 1 ''')
        arcpy.Select_analysis("fc_temp", out_fc, ''' "NewestFDate" = 1 ''')
        arcpy.DeleteField_management(out_fc, "NewestFDate")
        # arcpy.Merge_management(["newest_only", "unique"], out_fc)

##        for intermediate in ["freqtable", "dupetable", "fc_temp", "newest_only"]:
##            arcpy.Delete_management(intermediate)

    else:
        cu.multi_msg(
            "There were no remaining duplicates with differing FDates.")
        arcpy.CopyFeatures_management(in_fc, out_fc)
def lakes_in_zones(zones_fc, zone_field, lakes_fc, output_table):

    # make sure we're only using the right types of lakes, our feature
    # class excludes everything else but this is a guarantee this will
    # get checked at some point
    arcpy.env.workspace = 'in_memory'

    # using in_memory workspace means no SHAPE@AREA attribute later so I
    # need to calculate another field with the area using temp on disk
    # then go ahead and copy to memory, delete temp
    temp_workspace = cu.create_temp_GDB('lakezone')
    temp_lakes = os.path.join(temp_workspace, 'temp_lakes')
    arcpy.CopyFeatures_management(lakes_fc, temp_lakes)
    arcpy.AddField_management(temp_lakes, 'Area_ha', 'DOUBLE')
    arcpy.CalculateField_management(temp_lakes, 'Area_ha', '!shape.area@hectares!', 'PYTHON')

    # this bit enforces the correct lake type/size restriction just in case
    # geodata doesn't have this filtered already
    need_selection = False
    fcodes = (39000, 39004, 39009, 39010, 39011, 39012,
                43600, 43613, 43615, 43617, 43618, 43619, 43621)
    with arcpy.da.SearchCursor(temp_lakes, ["FCode"]) as cursor:
            for row in cursor:
                if row[0] not in fcodes + (43601,):
                    need_selection = True

    if need_selection:
        whereClause = '''
                    ("AreaSqKm" >=0.04 AND "FCode" IN %s)\
                    OR ("AreaSqKm" >= 0.1 AND "FCode" = 43601)''' % (fcodes,)
        arcpy.Select_analysis(temp_lakes, "lakes_4ha", whereClause)
        temp_lakes = os.path.join(arcpy.env.workspace, "lakes_4ha")


    selections = [""""Area_ha" >= 4""",
                """"Area_ha" >= 4 AND "Connection" = 'Isolated'""",
                """"Area_ha" >= 4 AND "Connection" = 'Headwater'""",
                """"Area_ha" >= 4 AND "Connection" = 'DR_Stream'""",
                """"Area_ha" >= 4 AND "Connection" = 'DR_LakeStream'""",
                """"Area_ha" >= 4 AND "Area_ha" < 10""",
                """"Area_ha" >= 4 AND "Area_ha" < 10 AND "Connection" = 'Isolated'""",
                """"Area_ha" >= 4 AND "Area_ha" < 10 AND "Connection" = 'Headwater'""",
                """"Area_ha" >= 4 AND "Area_ha" < 10 AND "Connection" = 'DR_Stream'""",
                """"Area_ha" >= 4 AND "Area_ha" < 10 AND "Connection" = 'DR_LakeStream'""",
                """"Area_ha" >= 10""",
                """"Area_ha" >= 10 AND "Connection" = 'Isolated'""",
                """"Area_ha" >= 10 AND "Connection" = 'Headwater'""",
                """"Area_ha" >= 10 AND "Connection" = 'DR_Stream'""",
                """"Area_ha" >= 10 AND "Connection" = 'DR_LakeStream'"""
                ]
    temp_tables = ['Lakes4ha',
                'Lakes4ha_Isolated',
                'Lakes4ha_Headwater',
                'Lakes4ha_DRStream',
                'Lakes4ha_DRLakeStream',
                'Lakes4to10ha',
                'Lakes4to10ha_Isolated',
                'Lakes4to10ha_Headwater',
                'Lakes4to10ha_DRStream',
                'Lakes4to10ha_DRLakeStream',
                'Lakes10ha',
                'Lakes10ha_Isolated',
                'Lakes10ha_Headwater',
                'Lakes10ha_DRStream',
                'Lakes10ha_DRLakeStream'
                ]

    for sel, temp_table in zip(selections, temp_tables):
        cu.multi_msg("Creating temporary table called {0} for lakes where {1}".format(temp_table, sel))
        polygons_in_zones.polygons_in_zones(zones_fc, zone_field, temp_lakes, temp_table, sel)
        new_fields = ['Poly_Overlapping_AREA_ha', 'Poly_Contributing_AREA_ha', 'Poly_Overlapping_AREA_pct', 'Poly_Count']
        avg_size_field = temp_table + '_AvgSize_ha'
        arcpy.AddField_management(temp_table, avg_size_field , 'DOUBLE')
        arcpy.CalculateField_management(temp_table, avg_size_field, '!Poly_Contributing_AREA_ha!/!Poly_Count!', 'PYTHON')
        for f in new_fields:
            cu.rename_field(temp_table, f, f.replace('Poly', temp_table), True)

    # join em up and copy to final
    temp_tables.remove('Lakes4ha')
    for t in temp_tables:
        try:
            arcpy.JoinField_management('Lakes4ha', zone_field, t, zone_field)
        #sometimes there's no table if it was an empty selection
        except:
            empty_fields = [f.replace('Poly', t) for f in new_fields]
            for ef in empty_fields:
                arcpy.AddField_management('Lakes4ha', ef, 'Double')
                arcpy.CalculateField_management('Lakes4ha', ef, '0', 'PYTHON')
            continue

    # remove all the extra zoneID fields, which have underscore in name
    drop_fields = [f.name for f in arcpy.ListFields('Lakes4ha', 'ZoneID_*')]
    for f in drop_fields:
        arcpy.DeleteField_management('Lakes4ha', f)
    arcpy.CopyRows_management('Lakes4ha', output_table)

    # clean up
    for item in ['Lakes4ha', temp_lakes, os.path.dirname(temp_workspace)] + temp_tables:
        arcpy.Delete_management(item)
def colorPolygons(feature_class, feature_field, out_gdb):
    arcpy.env.overwriteOutput = True

##    # Create temporary directory
##    temp_dir = os.path.join(tempfile.gettempdir(), 'zonal')
##    index = 0
##    while os.path.exists(temp_dir):
##        temp_dir = os.path.join(tempfile.gettempdir(), 'zonal%d' % index)
##        index += 1
##    os.mkdir(temp_dir)


    # Initialize variables
    temp_features = 'in_memory/dissolve'
    bldissolved = False
    # Dissolve on non-ObjectID field
    desc = arcpy.Describe(feature_class)
    cu.multi_msg("Dissolving features.")
    if hasattr(desc, "OIDFieldName"):
        if feature_field != desc.OIDFieldName:
            arcpy.Dissolve_management(feature_class, temp_features, \
                feature_field)
            bldissolved = True
        else:
            temp_features = feature_class
    else:
        arcpy.Dissolve_management(feature_class, temp_features, \
            feature_field)
        bldissolved = True
    # Get ObjectID field from dissolved
    if bldissolved:
        desc = arcpy.Describe(temp_features)
        oid_field = desc.OIDFieldName
    else:
        oid_field = feature_field

    # Calculate polygon contiguity
    cu.multi_msg("Identifying overlapping polygons...")
    arcpy.env.outputMFlag = "Disabled"
    result = arcpy.PolygonNeighbors_analysis(temp_features,
        'in_memory/neighbors', oid_field, "AREA_OVERLAP", "BOTH_SIDES")
    if 'WARNING 000117:' in result.getMessages(1):
        arcpy.AddError("Input feature zone data: {} does not contain "
                        "overlapping features.".format(temp_features))
        sys.exit(1)

    cu.multi_msg("Identified overlapping polygons.")
    cu.multi_msg("Calculating feature subsets without overlaps...")

    # Retrieve as array with columns src_FID and nbr_FID
    arr = arcpy.da.TableToNumPyArray('in_memory/neighbors',
        ['src_%s' % oid_field, 'nbr_%s' % oid_field])
    arr = numpy.array(arr.tolist())

    # Retrieves the colors of the neighboring nodes
    def get_colors(nodes, neighbors):
        colors = set()
        for neighbor in neighbors:
            colors.add(nodes[neighbor][0])
        colors.difference([0])
        return colors

    # Create a new color
    def get_new_color(colors):
        return max(colors)+1 if len(colors) > 0 else 1

    # Chooses from existing colors randomly
    def choose_color(colors):
        return random.choice(list(colors))

    # Sort source FIDs in descending order by number of neighbors
    arr_uniq = numpy.unique(arr[:,0])
    arr_count = numpy.zeros_like(arr_uniq)
    for index in range(arr_uniq.size):
        arr_count[index] = numpy.count_nonzero(arr[:, 0] == arr_uniq[index])
    arr_ind = numpy.argsort(arr_count)[::-1]

    # Initialize node dictionary --
    #   where key := FID of feature (integer)
    #   where value[0] := color of feature (integer)
    #   where value[1] := FIDs of neighboring features (set)
    nodes = collections.OrderedDict()
    for item in arr_uniq[arr_ind]:
        nodes[item] = [0, set()]
    # Populate neighbors
    for index in range(arr.shape[0]):
        nodes[arr[index, 0]][1].add(arr[index, 1])

    # Color nodes --
    colors = set()
    for node in nodes:
        # Get colors of neighboring nodes
        nbr_colors = get_colors(nodes, nodes[node][1])
        # Search for a color not among those colors
        choices = colors.difference(nbr_colors)
        # Assign the node that color or create it when necessary
        if len(choices) == 0:
            new_color = get_new_color(colors)
            colors.add(new_color)
            nodes[node][0] = new_color
        else:
            nodes[node][0] = choose_color(choices)

    # Classify nodes by colors --
    classes = {}
    for node in nodes:
        color = nodes[node][0]
        if color in classes:
            classes[color].add(node)
        else:
            classes[color] = set([node])

    # Get set of all FIDs
    all_fids = set()
    with arcpy.da.SearchCursor(temp_features, oid_field) as cursor:
        for row in cursor:
            all_fids.add(row[0])

    # Add disjoint FIDs to new class if necessary
    disjoint_fids = all_fids.difference(set(nodes.keys()))
    if len(disjoint_fids) > 0:
        new_color = get_new_color(colors)
        classes[new_color] = disjoint_fids

    # Calculate number of classes
    num_classes = len(classes)

    # Save each class
    temp_lyr = "temp_layer"
    cl_separator = ' OR \"%s\" = ' % oid_field
    for index, cl in enumerate(classes):
##        arcpy.SetProgressorLabel(
##            "Processing layer %d of %d..." % (index+1, num_classes))
        test = tuple(map(int, classes[cl]))
        where_clause = '\"%s\" IN %s' % (oid_field, \
            test)
        arcpy.MakeFeatureLayer_management(temp_features, temp_lyr, \
            where_clause)

        # This part added by Nicole
        # Save polygon layers to output gdb
        outLayerBase = os.path.splitext(os.path.basename(feature_class))[0]
        outLayerName = arcpy.CreateUniqueName(outLayerBase + "_NoOverlap", out_gdb)
        cu.multi_msg("Saving feature class %s of %s with name %s" % (str(index + 1),
            str(num_classes), outLayerName))
##        arcpy.Select_analysis(temp_features, outLayerName, where_clause)

        count = arcpy.GetCount_management(temp_lyr).getOutput(0)
        cu.multi_msg("count of features: " + str(count))
        arcpy.CopyFeatures_management(temp_lyr, outLayerName)
        arcpy.Delete_management(temp_lyr)
    return out_gdb
def wetland_order(rivex, stream_area_fc, nwi, out_fc):
    arcpy.env.workspace = 'in_memory'
    arcpy.env.outputCoordinateSystem = arcpy.SpatialReference(102039)
    arcpy.env.extent = nwi

    # Buffer the wetland perimeters by 30 meters
    cu.multi_msg('Creating 30m wetland buffers...')
    arcpy.Buffer_analysis(nwi, "wetland_buffers", "30 meters", "OUTSIDE_ONLY")
    arcpy.env.extent = "wetland_buffers"

    cu.multi_msg('Preparing for river line and area merge...')
    arcpy.CopyFeatures_management(rivex, 'rivex_extent')
    arcpy.CopyFeatures_management(stream_area_fc, 'stream_area_extent')
    arcpy.MakeFeatureLayer_management('rivex_extent', 'rivex_lyr')
    arcpy.SelectLayerByLocation_management('rivex_lyr', 'COMPLETELY_WITHIN',
                                           stream_area_fc)
    arcpy.CopyFeatures_management('rivex_lyr', 'rivex_for_splitting')
    arcpy.SelectLayerByAttribute_management('rivex_lyr', 'SWITCH_SELECTION')
    arcpy.CopyFeatures_management('rivex_lyr', 'rivex_not_areas')
    split_strahler('stream_area_extent', 'rivex_for_splitting',
                   'stream_area_split')

    # areas TO lines
    arcpy.PolygonToLine_management('stream_area_split', 'streamarea_to_line',
                                   False)

    # Merge features together
    arcpy.Merge_management(['streamarea_to_line', 'rivex_not_areas'],
                           'merged_rivers', 'NO_TEST')

    # FOR THE LINE-BASED PORTION
    # Spatial join connected wetlands and streams
    ##################Field Maps########################
    fms = arcpy.FieldMappings()
    fm_strahlermax = arcpy.FieldMap()
    fm_strahlersum = arcpy.FieldMap()
    fm_lengthkm = arcpy.FieldMap()
    fm_wetid = arcpy.FieldMap()

    fm_strahlermax.addInputField('merged_rivers', "Strahler")
    fm_strahlersum.addInputField('merged_rivers', "Strahler")
    fm_lengthkm.addInputField('merged_rivers', "LengthKm")
    fm_wetid.addInputField("wetland_buffers", "WET_ID")

    fm_lengthkm.mergeRule = 'Sum'
    fm_strahlermax.mergeRule = 'Max'
    fm_strahlersum.mergeRule = 'Sum'

    lengthkm_name = fm_lengthkm.outputField
    lengthkm_name.name = 'StreamKm'
    lengthkm_name.aliasName = 'StreamKm'
    fm_lengthkm.outputField = lengthkm_name

    strahlermax_name = fm_strahlermax.outputField
    strahlermax_name.name = 'StrOrdMax'
    strahlermax_name.aliasName = 'StrOrdMax'
    fm_strahlermax.outputField = strahlermax_name

    strahlersum_name = fm_strahlersum.outputField
    strahlersum_name.name = 'StrOrdSum'
    strahlersum_name.aliasName = 'StrOrdSum'
    fm_strahlersum.outputField = strahlersum_name

    fms.addFieldMap(fm_strahlermax)
    fms.addFieldMap(fm_strahlersum)
    fms.addFieldMap(fm_lengthkm)
    fms.addFieldMap(fm_wetid)
    #####################################################

    arcpy.SpatialJoin_analysis("wetland_buffers", 'merged_rivers',
                               "wetland_spjoin_streams", '', '', fms)

    # Get the stream count from the join count
    cu.rename_field("wetland_spjoin_streams", 'Join_Count', "StreamCnt", True)

    # Join the new fields back to the original feature class based on WET_ID
    join_fields = ['StrOrdMax', 'StrOrdSum', 'StreamKm', 'StreamCnt']
    arcpy.CopyFeatures_management(nwi, out_fc)
    arcpy.JoinField_management(out_fc, 'WET_ID', 'wetland_spjoin_streams',
                               'WET_ID', join_fields)

    # Set these to 0 where there is no connection
    cu.redefine_nulls(out_fc, join_fields, [0, 0, 0, 0])

    # Classify VegType: 4 options based on class code in ATTRIBUTE field
    arcpy.AddField_management(out_fc, "VegType", "TEXT")
    with arcpy.da.UpdateCursor(out_fc, ["ATTRIBUTE", "VegType"]) as cursor:
        for row in cursor:
            attr_abbv = row[0][:3]
            if attr_abbv == "PEM" or attr_abbv == "PAB":
                row[1] = "PEMorPAB"
            elif attr_abbv == "PFO":
                row[1] = "PFO"
            elif attr_abbv == "PSS":
                row[1] = "PSS"
            else:
                row[1] = "Other"
            cursor.updateRow(row)

    # Determine the regime from the letter code. Examples: PSS1E ---> E,
    #  PEM1/SS1Fb --> F
    class_codes = 'RB UB AB US ML EM SS FO'.split()
    regime_codes = 'A B C E F G H J K'.split()
    arcpy.AddField_management(out_fc, "Regime", "TEXT")
    with arcpy.da.UpdateCursor(out_fc, ["ATTRIBUTE", "Regime"]) as cursor:
        for row in cursor:
            # All the wetlands are already palustrine, so if we remove the class
            # codes, any capital letters left besides the P in front
            # are the regime code
            # example codes: PEM1E, PSS1/EM1E, PEM1/5C, PUSA, PSSf
            # If you ever can figure out the regex for this instead, go ahead.
            code_value = row[0]
            regime_value = 'unknown'
            # this bit searches for the class codes and replaces them with nothing
            # this is necessary because meaning of A, B, E, F is context dependent
            for class_code in class_codes:
                if class_code in code_value:
                    code_value = code_value.replace(class_code, '')
            for char in code_value:
                if char in regime_codes:
                    regime_value = char
            row[1] = regime_value
            cursor.updateRow(row)

    # Calculate WetOrder from StrOrdSum
    arcpy.AddField_management(out_fc, "WetOrder", "TEXT")
    with arcpy.da.UpdateCursor(out_fc, ["StrOrdSum", "WetOrder"]) as cursor:
        for row in cursor:
            if row[0] == 0:
                row[1] = "Isolated"
            elif row[0] == 1:
                row[1] = "Single"
            elif row[0] == None:
                row[1] = "Isolated"
            else:
                row[1] = "Connected"
            cursor.updateRow(row)
    arcpy.Delete_management('in_memory')
Beispiel #44
0
def line_density(zones, zonefield, lines, out_table, interest_selection_expr):
    # Make output folder
    ##    name = "LineDensity_" + os.path.splitext(os.path.basename(zones))[0]
    ##    outfolder = os.path.join(topoutfolder, name)
    ##    if not os.path.exists(outfolder):
    ##        os.mkdir(outfolder)

    # Environmental Settings
    ws = "in_memory"

    if interest_selection_expr:
        arcpy.MakeFeatureLayer_management(lines, "selected_lines",
                                          interest_selection_expr)
    else:
        arcpy.MakeFeatureLayer_management(lines, "selected_lines")

    arcpy.env.workspace = ws
    albers = arcpy.SpatialReference(102039)
    arcpy.env.outputCoordinateSystem = albers
    arcpy.env.extent = zones

    # Zones will be coerced to albers, have to check lines though
    arcpy.CopyFeatures_management(zones, "zones_temp")
    if arcpy.Describe(
            lines).spatialReference.factoryCode != albers.factoryCode:
        arcpy.AddError(
            "Lines feature class does not have desired projection (Albers USGS). Re-project to factory code 102039 and try again."
        )
        sys.exit(1)

    # Add hectares field to zones
    arcpy.AddField_management("zones_temp", "ZoneAreaHa", "DOUBLE")
    arcpy.CalculateField_management("zones_temp", "ZoneAreaHa",
                                    "!shape.area@hectares!", "PYTHON")

    # Perform identity analysis to join fields and crack lines at polygon boundaries
    cu.multi_msg("Cracking lines at polygon boundaries...")
    arcpy.Identity_analysis("selected_lines", "zones_temp", "lines_identity")
    cu.multi_msg("Cracking lines complete.")

    # Recalculate lengths
    arcpy.AddField_management("lines_identity", "LengthM", "DOUBLE")
    arcpy.CalculateField_management("lines_identity", "LengthM",
                                    '!shape.length@meters!', "PYTHON")

    # Summarize statistics by zone
    arcpy.Statistics_analysis("lines_identity", "length_in_zone",
                              "LengthM SUM", zonefield)

    # Join ZoneAreaHa to table
    arcpy.JoinField_management("length_in_zone", zonefield, "zones_temp",
                               zonefield, "ZoneAreaHa")

    # Delete rows in table with zero for zone area
    ##    with arcpy.da.UpdateCursor("length_in_zone", "ZoneAreaHa") as cursor:
    ##        for row in cursor:
    ##            if row[0] is None:
    ##                cursor.deleteRow()

    # Add Density field and calc
    arcpy.AddField_management("length_in_zone", "Density_MperHA", "DOUBLE", '',
                              '', '', '', "NULLABLE")
    exp = "!SUM_LengthM! / !ZONEAREAHA!"
    arcpy.CalculateField_management("length_in_zone", "Density_MperHA", exp,
                                    "PYTHON")

    cu.one_in_one_out("length_in_zone", ['SUM_LengthM', 'Density_MperHA'],
                      zones, zonefield, out_table)
    cu.redefine_nulls(out_table, ['SUM_LengthM', 'Density_MperHA'], [0, 0])

    ##    # Join to the original table
    ##    keep_fields = ["ZoneID", "SUM_LengthM", "Density_MperHA"]
    ##    arcpy.JoinField_management('zones_temp', zonefield, "length_in_zone", zonefield, keep_fields[1:])
    ##
    ##    # Select only null records and change to 0
    ##    arcpy.MakeFeatureLayer_management('zones_temp', 'zones_temp_lyr')
    ##    arcpy.SelectLayerByAttribute_management('zones_temp_lyr', "NEW_SELECTION", '''"SUM_LengthM" is null''')
    ##    fields_to_calc = ["SUM_LengthM", "Density_MperHA"]
    ##    for f in fields_to_calc:
    ##        arcpy.CalculateField_management('zones_temp_lyr', f, 0, "PYTHON")
    ##
    ##    #Delete all the fields that aren't the ones I need
    ##    keep_fields = ["ZoneID", "SUM_LengthM", "Density_MperHA"]
    ##    all_fields = [f.name for f in arcpy.ListFields('zones_temp_lyr')]
    ##    for f in all_fields:
    ##        if f not in keep_fields:
    ##            try:
    ##                arcpy.DeleteField_management('zones_temp_lyr', f)
    ##            except:
    ##                continue
    ##    arcpy.SelectLayerByAttribute_management('zones_temp_lyr', 'CLEAR_SELECTION')
    ##
    ##    arcpy.CopyRows_management('zones_temp_lyr', out_table)

    for tempitem in ['zones_temp', 'lines_identity', 'length_in_zone']:
        arcpy.Delete_management(tempitem)

    return out_table