Geographic Information Systems Asked by NamuDude on December 3, 2020
I am somewhat familiar with using the python language in the Calculate Field tool in ArcGIS Pro, but was wondering how this is done in a stand alone Python script with a raster .img (Combine20012004FltPths.img) file.
I am trying to reclassify a new field I have created using integer values from another field. However, when I try to run the code in IDLE, I get a "SyntaxError: invalid syntax".
Here is my python script for the Code Block and the arcpy management Calculate Field tool:
# Code Block for Calculate Field
codeBlock = def reclass(FlightPat1):
if (FlightPat1 == 0):
return "Unclassified"
elif (FlightPat1 == 11):
return "Open Water"
elif (FlightPat1 == 12):
return "Perennial Snow/Ice"
elif (FlightPat1 == 21):
return "Developed, Open Space"
elif (FlightPat1 == 22):
return "Developed, Low Intensity"
elif (FlightPat1 == 23):
return "Developed, Medium Intensity"
elif (FlightPat1 == 24):
return "Developed, High Intensity"
elif (FlightPat1 == 31):
return "Barren Land"
elif (FlightPat1 == 41):
return "Deciduous Forest"
elif (FlightPat1 == 42):
return "Evergreen Forest"
elif (FlightPat1 == 43):
return "Mixed Forest"
elif (FlightPat1 == 52):
return "Shrub/Scrub"
elif (FlightPat1 == 71):
return "Herbaceuous"
elif (FlightPat1 == 81):
return "Hay/Pasture"
elif (FlightPat1 == 82):
return "Cultivated Crops"
elif (FlightPat1 == 90):
return "Woody Wetlands"
elif (FlightPat1 == 95):
return "Emergent Herbaceuous Wetlands"
#Reclassify field based off another field
arcpy.CalculateField_management("Combine20012004FltPths.img", "NLCD01Clss",
"Reclass(!FlightPat1!)", expression_type = "PYTHON3",
code_block = codeBlock, field_type = "TEXT")
Would I need to convert the attribute table of my .img (Combine20012004FltPths.img) file into a dBASE or geodatabase table or some other table format, then run the Code Block and Calculate Field tool for it to work?
The key to providing a code block to CalculateField
is to understand that a separate Python parser is used inside the utility. The code_block
must therefore be a string containing viable code. You can accomplish this by using triple-quotes around the text, so that Python handles line continuation correctly:
# Code Block for Calculate Field
codeBlock = """
def reclass(flightPat1): # Note lowercase variable name -- upcase is for Classes
{rest of function}
"""
But that's only the first iteration of improvement. There are two basic approaches to coding a switchyard function. The first uses a cascade of conditional tests, and has two major forms: With and without early exit. First without:
# Code Block for Calculate Field
codeBlock = """
def reclass(flightPat1):
result = None #! Initialize so that it always returns a default value
if (flightPat1 == 0):
result = "Unclassified"
elif (flightPat1 == 11):
result = "Open Water"
...
return result
"""
The early exit form should be familiar, but given that return
is used, no else
is needed:
# Code Block for Calculate Field
codeBlock = """
def reclass(flightPat1):
if (flightPat1 == 0):
return "Unclassified"
if (flightPat1 == 11):
return "Open Water"
...
return None #! Suffices for final else
"""
The second approach leverages the dictionary datatype. It is faster, and more pythonic:
# Code Block for Calculate Field
codeBlock = """
lookup = {
0 : "Unclassified",
11 : "Open Water",
...
}
def reclass(flightPat1):
return lookup[flightPat1] if flightPat1 in lookup else None
"""
The sexy part about dictionary use is that you can combine this with the fact that you're just compiling a string here, so you can assemble it dynamically (in this case, from a file geodatabase table):
# Code Block for Calculate Field
lookupSrc = r"C:Tempgis_se.gdbanswer_lookup"
codeBlock = """
def reclass(flightPat1):
lookup = {
@TERMS
}
return lookup[flightPat1] if flightPat1 in lookup else None
""".replace('@TERMS',',n'.join(
["{:10d} : '{:s}'".format(rec[0],rec[1].replace("'","'"))
for rec in arcpy.da.SearchCursor(lookupSrc,['ival','sval'])]))
Okay, so yeah, that's a bit hot-n-heavy, but how many times do you get to do a list comprehension on a cursor, a format, and two replacements in one extended line? And it does work:
>>> print(codeBlock)
def reclass(flightPat1):
lookup = {
0 : 'Unclassified',
11 : 'Open Water',
12 : 'Perennial Snow/Ice',
21 : 'Developed, Open Space',
22 : 'Developed, Low Intensity',
23 : 'Developed, Medium Intensity',
24 : 'Developed, High Intensity',
31 : 'Barren Land',
41 : 'Deciduous Forest',
42 : 'Evergreen Forest',
43 : 'Mixed Forest',
52 : 'Shrub/Scrub',
71 : 'Herbaceuous',
81 : 'Hay/Pasture',
82 : 'Cultivated Crops',
90 : 'Woody Wetlands',
94 : 'Apostrophe's Example',
95 : 'Emergent Herbaceuous Wetlands'
}
return lookup[flightPat1] if flightPat1 in lookup else None
The obtuse part could be de-inlined to self-document:
# Code Block for Calculate Field
lookupSrc = r"C:Tempgis_se.gdbanswer_lookup"
codeFormat = """
def reclass(flightPat1):
lookup = {
@TERMS
}
return lookup[flightPat1] if flightPat1 in lookup else None
"""
termList = []
with arcpy.da.SearchCursor(lookupSrc,['ival','sval']) as cursor:
for row in cursor:
ival = row[0]
sval = row[1].replace("'","'") #! Handle case where sval contains apostrophe
term = "{:10d} : '{:s}'".format(ival,sval)
termList.append(term)
termString = ',n'.join(termList) #! str.join() adds text between list elements
codeBlock = codeFormat.replace('@TERMS',termString)
Correct answer by Vince on December 3, 2020
Get help from others!
Recent Questions
Recent Answers
© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP