1
import arcpy
import os.path
arcpy.env.workspace = "C:/Users/melis/Documents/HenryWork.gdb"
arcpy.env.overwriteOutput = True
buffer = "LS_buffer"
landcov = "ForestLC_clip"

#create search cursor to access table. may need to change field name with other data.
buffCursor = arcpy.da.SearchCursor(buffer, ["buff_id"])

for row in buffCursor:
    #select one buffer
    query = """ "buff_id" = %s """ %row[0]
    arcpy.SelectLayerByAttribute_management(buffer, "NEW_SELECTION", query)

    #define clip out path
    clip_out = "LS" + str(row[0])

    #clip w one buffer
    arcpy.Clip_analysis(landcov, buffer, clip_out)

    #add fields to clip_out
    arcpy.AddField_management(clip_out, "clip_id", "TEXT", 20)
    arcpy.AddField_management(clip_out, "area_ha", "FLOAT")

    #create updatecursor for new clip_out
    clipCursor = arcpy.da.UpdateCursor(clip_out, ["clip_id", "area_ha"])

    for polys in clipCursor:

        #calc geom for area ha
        exp = "!SHAPE.AREA@HECTARES!"
        arcpy.CalculateField_management(clip_out, "area_ha", exp, "PYTHON_9.3")

        #field calc for buffer id
        polys[0] = row[0]

del clipCursor

The code works all the way down to the first calculate field, and then I get the error: "Cannot acquire a lock". However, when I test whether I can acquire a lock on that feature class (the new clip_out ("LS1"), I get a "True".

arcpy.TestSchemaLock("LS1")
True

Any idea why the cursor code can't acquire a lock, but when outside the code, I can acquire the lock?

Melissa
  • 165
  • 1
  • 9
  • 4
    Probably because you are using CalculateField inside an UpdateCursor. Normally you would do use one or the other, not both at the same time. – Andy Dec 24 '18 at 00:09

1 Answers1

3

As @Andy said, you're using calculate field in a double cursor loop, it's the cursor that's locking the calculate field.. instead try it like this:

#create search cursor to access table. may need to change field name with other data.
with arcpy.da.SearchCursor(buffer, ["buff_id"]) as buffCursor:
    for row in buffCursor:
            #select one buffer
            query = """ "buff_id" = %s """ %row[0]
            arcpy.SelectLayerByAttribute_management(buffer, "NEW_SELECTION", query)

            #define clip out path
            clip_out = "LS" + str(row[0])

            #clip w one buffer
            arcpy.Clip_analysis(landcov, buffer, clip_out)

            #add fields to clip_out
            arcpy.AddField_management(clip_out, "clip_id", "TEXT", 20)
            arcpy.AddField_management(clip_out, "area_ha", "FLOAT")

            #create updatecursor for new clip_out
            with arcpy.da.UpdateCursor(clip_out, ["clip_id", "area_ha","SHAPE@AREA"]) as clipCursor:
                for polys in clipCursor:

                    #calc geom for area ha
                    polys[1] = polys[2].area / 10000 # A hectare is 10000 square metres

                    #field calc for buffer id
                    polys[0] = row[0]
                    clipCursor.updateRow(polys) # IMPORTANT don't forget to commit the update

I've changed your cursors to with blocks, this handles the removing when the cursor passes out of scope. I am assuming here that your data is in a spatial reference with units in metres, see this post about other units (like feet) for a simple conversion but if your data is in geographical units you can choose a suitable projected coordinate system (for example WGS84 UTM Zone 55 south, EPSG code 32755) and project the geometry on the fly:

CalcSR = arcpy.SpatialReference(32755) # declare this outside the loop using EPSG code
with arcpy.da.UpdateCursor(clip_out, ["clip_id", "area_ha","SHAPE@"]) as clipCursor:
    for polys in clipCursor:
        #calc geom for area ha, project to UTM and calculate from the area
        polys[1] = polys[2].projectAs(CalcSR).area / 10000 # A hectare is 10000 square metres

        #field calc for buffer id
        polys[0] = row[0]
        clipCursor.updateRow(polys) # IMPORTANT don't forget to commit the update

Either way though the use of a cursor-in-cursor loop is discouraged, it's better to build a list and then iterate:

#create list to access table. may need to change field name with other data.
# builds a list of all buff_id values... you might want to do something about
# removing duplicates but this works as an example
AllBuffID = [r[0] for r in arcpy.da.SearchCursor(buffer, ["buff_id"])]

for thisBuffID in AllBuffID:
        #select one buffer
        query = """ "buff_id" = %s """ %thisBuffID
        arcpy.SelectLayerByAttribute_management(buffer, "NEW_SELECTION", query)

        #define clip out path
        clip_out = "LS" + str(thisBuffID)

        #clip w one buffer
        arcpy.Clip_analysis(landcov, buffer, clip_out)

        #add fields to clip_out
        arcpy.AddField_management(clip_out, "clip_id", "TEXT", 20)
        arcpy.AddField_management(clip_out, "area_ha", "FLOAT")

        #create updatecursor for new clip_out
        with arcpy.da.UpdateCursor(clip_out, ["clip_id", "area_ha","SHAPE@"]) as clipCursor:
            for polys in clipCursor:
                #calc geom for area ha, project to UTM and calculate from the area
                polys[1] = polys[2] / 10000 # A hectare is 10000 square metres

                #field calc for buffer id
                polys[0] = thisBuffID
                clipCursor.updateRow(polys) # IMPORTANT don't forget to commit the update
Michael Stimson
  • 25,566
  • 2
  • 35
  • 74
  • thanks for the thoughtful answer. I finally had some time to get back to this and have a followup question.

    I got an error at line 30 (polys[1] = polys[2] / 10000). I changed it to polys[1] = (polys[2].area) / 10000, which fixed the error.

    However, there's another error I can't fix. It only went through one iteration of the for loop and then stopped, meaning I only got one feature class output called "LS1". How do I get it to give me the rest?

    Thanks

    – Melissa Jan 04 '19 at 20:21
  • Well spotted, you're right. The script is written for a single iteration, to do all feature classes in a workspace set your arcpy.env.workspace to the folder/gdb you need to loop through and use for buffer in arcpy.ListFeatureClasses(feature_type='Polygon'): (don't forget to indent the code) to iterate all of the polygon feature classes. Have a look at the examples https://resources.arcgis.com/en/help/main/10.2/index.html#//03q300000023000000 and if you are still having problems post a new question with a link to this post. – Michael Stimson Jan 07 '19 at 00:36