How to keep missing fields from crashing my code?

1234
13
06-12-2012 07:41 AM
RichardThurau
Occasional Contributor
Hi,
I have a very helpful code I use to run metrics between land cover and a bunch of assessment boundary feature classes using tabulate area. I put my land cover classes in a list and the field names that are calculated from the classes in another list. Update cursor fills in the rest.

The problem is, if I have an assessment boundary that does not intersect a land cover class, when update cursor goes to calculate the field from the class value, it crashes the code.

I will paste a sample code:

ClassList = ["Forest", "Vegetation"]
FieldList = ["Land_Acres","Forest_Acres", "Forest_Percent", "Veg_Acres", "Veg_Percent"]

m2ac = 0.0002471054

FcList = arcpy.ListFeatureClasses("*", "")   
for fc in FcList:
    areaTab1 = "Metrics_" + os.path.basename(fc)
    for field in FieldList:
        arcpy.AddField_management(fc, field, "DOUBLE")
    TabulateArea(fc, "GID", LC, "Class", areaTab1 , 1) #"UTC_Metrics_" + os.path.basename(fc)
    arcpy.JoinField_management(fc, "GID", areaTab1, "GID", ClassList)#"trees11_tab_" + os.path.basename(fc)
    rows = arcpy.UpdateCursor(fc)
    print str(fc)
    for row in rows:  
        row.setValue(FieldList[0], (row.Shape_Area * m2ac))#Total Acres
        rows.updateRow(row)
        row.setValue(FieldList[1], row.getValue(ClassList[0]) * m2ac) #Forest Acres
        rows.updateRow(row)
        row.setValue(FieldList[2], (row.getValue(FieldList[1]) / row.Land_Acres) * 100) #Forest Percent
        rows.updateRow(row)
        row.setValue(FieldList[3], row.getValue(ClassList[1]) * m2ac) #Veg Acres
        rows.updateRow(row)
        row.setValue(FieldList[4], (row.getValue(FieldList[3]) / row.Land_Acres) * 100) #Veg Percent
        rows.updateRow(row)
    del row
    del rows


So, this code runs great, unless one of the feature classes does not intersect with one of the land cover classes. Then, when the code calls the ClassList for that value, the code will crash in two spots:
1. At the joinfield, where it looking for all classes from the class list to join
2. In the update cursor when it calls the class name to calculate the fields.

Is there  a good way to make the code just skip the value rather than crashing the code?

Thanks

Rich
#
Tags (2)
0 Kudos
13 Replies
MathewCoyle
Frequent Contributor
Do you want your script to process that feature class at all if it doesn't have the required parameters? You could use a try/except block to exit that iteration and go to the next, or you could use a list field check on your feature class to compare against your list of fields that you want to be there.
0 Kudos
RichardThurau
Occasional Contributor
Mathew Thanks,
I absolutely want the code to be able to just: 1. Join all available fields, and 2. Calculate all available fields. Just keep on trucking through the feature class loop even if one feature class does not intersect with a land cover class.

I thought about a try/except for the update cursor, but I think that would still stop the code from calculating remaining fields. For example, if there was no forest, I'd still want it to calculate vegetation. Which a try/except would have to added to each line in the update cursor, correct?

But, I don't see a way to use try/except for the joinfield, where I will get a failure if my tabulate area table does not contain all fields within the ClassList.

rt
0 Kudos
MathewCoyle
Frequent Contributor
Probably not the best solution, but this should keep your loops running even if an individual iteration fails.
for fc in FcList:
    try:
        areaTab1 = "Metrics_" + os.path.basename(fc)
        for field in FieldList:
            arcpy.AddField_management(fc, field, "DOUBLE")
        TabulateArea(fc, "GID", LC, "Class", areaTab1 , 1) #"UTC_Metrics_" + os.path.basename(fc)
        arcpy.JoinField_management(fc, "GID", areaTab1, "GID", ClassList)#"trees11_tab_" + os.path.basename(fc)
        rows = arcpy.UpdateCursor(fc)
        print str(fc)
        for row in rows:
            try:
                row.setValue(FieldList[0], (row.Shape_Area * m2ac))#Total Acres
                rows.updateRow(row)
                row.setValue(FieldList[1], row.getValue(ClassList[0]) * m2ac) #Forest Acres
                rows.updateRow(row)
                row.setValue(FieldList[2], (row.getValue(FieldList[1]) / row.Land_Acres) * 100) #Forest Percent
                rows.updateRow(row)
                row.setValue(FieldList[3], row.getValue(ClassList[1]) * m2ac) #Veg Acres
                rows.updateRow(row)
                row.setValue(FieldList[4], (row.getValue(FieldList[3]) / row.Land_Acres) * 100) #Veg Percent
                rows.updateRow(row)
            except:
                pass
        del row
        del rows
    except:
        pass
0 Kudos
RichardThurau
Occasional Contributor
Mathew, you are correct. This will keep the code rolling, but doesn't really resolve the issue.

What would be awesome is have a classList based on the land cover. Regardless of what classes intersect, that classList will work. The sample code calculates values for five fields, but the real code has more than 30. The pain is in having to alter the code to match which land cover types intersect (getValue order from the classList).

One thing that would help, can I build the classList from the tabulate area table? Doing that would allow me to create a list that only contained the intersecting land cover types for each feature class. Then the try/except might work in the update cursor. Depends on where pass sends the code. Currently I think it just passes to the next feature class if any of the updateRow functions do not work.


Thanks for your thoughts.

rich
0 Kudos
GregBacon
New Contributor II

Richard,

Did you ever find a way to address this?  I'm doing the same thing with a tabulation of land cover types within watershed polygons.  Not all of my seven possible land cover types are present in each watershed, so the update cursor has problems with fields that may or may not be present.

Greg

0 Kudos
XanderBakker
Esri Esteemed Contributor

There are ways to avoid your code from crashing and only update those field that exist. I would probably use some dictionaries for this purpose and switch to the da cursors. If you are willing to drop a small part of your data I can have a look what I can do.

Kind regards, Xander

0 Kudos
GregBacon
New Contributor II

I'm posting a simplified table for this example.  The table is the result of the Tabulate Area tool and it will be inside a file gdb created by my model.  Let's just try to convert the square feet for each cover type to acres.  The problem is that you never know which of seven possible cover types you'll have in this table.  The path I've started down is this:

import arcpy

arcpy.env.workspace = r'G:\users\gis\gbacon\Watershed_Testing\valleyave\WatershedDelineation.gdb'

fldlist = arcpy.ListFields("TabSummary","","Double")

with arcpy.da.UpdateCursor("TabSummary",fldlist) as cursor:

    for row in cursor:

        row[0:] = row/43560

        cursor.updateRow(row)

The update cursor needs a list of fields, but the fldlist variable can't be used here.  The second issue seems to be in my use of row[0:] where I'm trying to say take each value from the first to the last of the possible seven and divide each by 43560.  I'm very new to Python and I appreciate your help.

0 Kudos
XanderBakker
Esri Esteemed Contributor

Hi Greg,

I'm afraid you added the XML and not the DBF file itself. Can you add it?

0 Kudos
XanderBakker
Esri Esteemed Contributor

If I read the DBF into a fgdb I will have a similar situation as you have?

0 Kudos