Stand alone คำนวณฟิลด์สคริปต์โดยใช้ Code Block สำหรับ IMG Raster ใน Python

Sep 03 2020

ฉันค่อนข้างคุ้นเคยกับการใช้ภาษา python ในเครื่องมือคำนวณฟิลด์ใน ArcGIS Pro แต่สงสัยว่าจะทำอย่างไรในสคริปต์ Python แบบสแตนด์อโลนพร้อมไฟล์ raster .img (Combine20012004FltPths.img)

ฉันกำลังพยายามจัดประเภทฟิลด์ใหม่ที่ฉันสร้างขึ้นโดยใช้ค่าจำนวนเต็มจากฟิลด์อื่น อย่างไรก็ตามเมื่อฉันพยายามเรียกใช้โค้ดใน IDLE ฉันได้รับ "SyntaxError: invalid syntax"

นี่คือสคริปต์ python ของฉันสำหรับ Code Block และการจัดการ arcpy 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")

ฉันจะต้องแปลงตารางแอตทริบิวต์ของไฟล์. img (Combine20012004FltPths.img) ของฉันเป็น dBASE หรือตารางฐานข้อมูลทางภูมิศาสตร์หรือรูปแบบตารางอื่น ๆ จากนั้นเรียกใช้เครื่องมือ Code Block และ Calculate Field เพื่อให้ทำงานได้หรือไม่

คำตอบ

3 Vince Sep 04 2020 at 16:07

กุญแจสำคัญในการจัดเตรียมบล็อกรหัสให้CalculateFieldคือการทำความเข้าใจว่ามีการใช้ตัวแยกวิเคราะห์ Python แยกต่างหากภายในยูทิลิตี้ code_blockต้องจึงจะเป็นสตริงที่มีรหัสที่ทำงานได้ คุณสามารถทำสิ่งนี้ให้สำเร็จได้โดยใช้เครื่องหมายอัญประกาศสามรอบข้อความเพื่อให้ Python จัดการความต่อเนื่องของบรรทัดได้อย่างถูกต้อง:

# Code Block for Calculate Field
codeBlock = """
def reclass(flightPat1):   # Note lowercase variable name -- upcase is for Classes
   {rest of function}
"""

แต่นั่นเป็นเพียงการปรับปรุงครั้งแรกเท่านั้น มีสองวิธีพื้นฐานในการเข้ารหัสฟังก์ชันสวิตช์ยาร์ด แบบแรกใช้การทดสอบตามเงื่อนไขแบบเรียงซ้อนและมีสองรูปแบบใหญ่ ๆ : มีและไม่มีทางออกก่อน ก่อนอื่นโดยไม่ต้อง:

# 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
"""

แบบฟอร์มการออกก่อนกำหนดควรคุ้นเคย แต่เมื่อreturnใช้แล้วไม่elseจำเป็น:

# 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
"""

แนวทางที่สองใช้ประโยชน์จากประเภทข้อมูลพจนานุกรม เร็วกว่าและ 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
"""

ส่วนที่เซ็กซี่เกี่ยวกับการใช้พจนานุกรมคือคุณสามารถรวมสิ่งนี้เข้ากับข้อเท็จจริงที่ว่าคุณกำลังรวบรวมสตริงที่นี่เพื่อให้คุณสามารถรวบรวมได้แบบไดนามิก (ในกรณีนี้จากตารางฐานข้อมูลของไฟล์):

# Code Block for Calculate Field
lookupSrc = r"C:\Temp\gis_se.gdb\answer_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'])]))

โอเคใช่แล้วมันค่อนข้างร้อนแรง แต่คุณจะทำความเข้าใจรายการบนเคอร์เซอร์รูปแบบและการแทนที่สองครั้งในบรรทัดเดียวได้กี่ครั้ง? และมันใช้งานได้:

>>> 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

ส่วนที่เป็นป้านอาจไม่เข้ากับเอกสารในตัว:

# Code Block for Calculate Field
lookupSrc = r"C:\Temp\gis_se.gdb\answer_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)