1

I am trying to update Z values of polygon features with point features. The code is (found on GIS Stack Exchange) working good if no 'interor ring' or 'multipart' features are present in the polygon features. The polygon is large data set with several 'interor rings' or 'multiparts'. I have no idea how to solve it. I have understood that 'NoneType' AttributeError refers to 'multipart' or 'inner rings' issue.

My code is :

import os
import arcpy

arcpy.env.workspace = r"D:\acrpy\VV_test\scripts\map1"
point_fc = r"D:\acrpy\VV_test\scripts\map1\VRTX_ZM_KAD_test.shp"
polygon_fc = r"\acrpy\VV_test\scripts\map1\ZM_KAD__test.shp"
my_field = "SHAPE@"
my_field_pnt = "SHAPE@XYZ"


with arcpy.da.UpdateCursor(polygon_fc, my_field) as myUpdtCur:
    for row in myUpdtCur:
        geom = row[0]
        arr_pol = arcpy.Array()
        for part in geom:            
            arr_part = arcpy.Array()
            pnt_count = 0
            for pnt in part:
                with arcpy.da.SearchCursor(point_fc, my_field_pnt) as mySrcCur:
                    for srcPnt in mySrcCur:
                        if int(pnt.X) == int(srcPnt[0][0]) and int(pnt.Y) == int(srcPnt[0][1]):
                            updZpnt=srcPnt[0][2]
                del mySrcCur
                pnt_count += 1                
                print pnt_count
                myXYZPoint = arcpy.Point(pnt.X, pnt.Y, updZpnt)
                arr_part.add(myXYZPoint)
            arr_pol.add(arr_part)
        polygon = arcpy.Polygon(arr_pol,None, True) 
        row[0] = polygon
        myUpdtCur.updateRow(row)
del myUpdtCur



got the error:
76
77
78
79
80
81
82

Traceback (most recent call last):
File "D:\acrpy\VV_test\scripts_OK\GIS_stackexcahnge\test_update Z values from point vertices to polygon vertices .py", line 21, in <module>
if int(pnt.X) == int(srcPnt[0][0]) and int(pnt.Y) == int(srcPnt[0][1]):
AttributeError: 'NoneType' object has no attribute 'X'
>>> 
  • Your entire nested SearchCursor is highly suspect; there is never a reason to do an unbounded search inside a loop. Remove the time wasted by that with a simple z=100 and focus on proper geometry processing. Also, you should never use the Polygon constructor without a valid SpatialReference parameter (doing so often corrupts the vertex stream). – Vince Jul 28 at 11:32
  • @Vince-Thank you for your reaction. I have got little knowledge in arcpy and python. Strangely the code is working good as i mentioned for non multipart polygon features. can you rectify the code and post here? – Ram Jul 28 at 11:42
1

The Reading geometries documentation shows the proper processing procedure for sub-parts in polygons:

for row in arcpy.da.SearchCursor(infc, ["OID@", "SHAPE@"]):
    # Print the current multipoint's ID
    #
    print("Feature {}:".format(row[0]))
    partnum = 0

    # Step through each part of the feature
    #
    for part in row[1]:
        # Print the part number
        #
        print("Part {}:".format(partnum))

        # Step through each vertex in the feature
        #
        for pnt in part:
            if pnt:
                # Print x,y coordinates of current point
                #
                print("{}, {}".format(pnt.X, pnt.Y))
            else:
                # If pnt is None, this represents an interior ring
                #
                print("Interior Ring:")
        partnum += 1

Note the if pnt test, with the comment

If pnt is None, this represents an interior ring

Some comments:

  • Your inner loop is so inefficient, I can't copy it in good conscience. This should be a simple matrix or sparse dictionary of arrays, populated once above the top of the code.

  • It should be a crime (misdemeanor data abuse) to not use the spatial_reference parameter in the arcpy.Polygon constructor -- failure to do so will truncate your coordinates to the third decimal place.

  • The with construct deletes variables, so your del statements were no-op.

And the corrected code block:

desc = arcpy.Describe(polygon_fc)
sr = desc.spatialReference
with arcpy.da.UpdateCursor(polygon_fc, my_field) as myUpdtCur:
    for row in myUpdtCur:
        geom = row[0]
        arr_pol = arcpy.Array()
        for part in geom:            
            arr_part = arcpy.Array()
            pnt_count = 0
            for pnt in part:
                if not pnt:
                    arr_part.add(None)    # Close previous subpart
                    continue     # <-- Skips hole markers
                ## assign updZpnt here
                pnt_count += 1                
                print pnt_count
                myXYZPoint = arcpy.Point(pnt.X, pnt.Y, updZpnt)
                arr_part.add(myXYZPoint)
            arr_pol.add(arr_part)

        row[0] = arcpy.Polygon(arr_pol,sr, True)
        myUpdtCur.updateRow(row)
  • Thank you. I amended the code and ran. It worked for only for ploygons with one "donut/innerring". I have got polygon features more than 4 "donuts/innerrings". How can i fix it? – Ram Jul 28 at 13:01
  • 1
    Argh. You may need to promote the subparts to parts and let the library figure it out. Hacking... Try that. – Vince Jul 28 at 13:23
  • 1
    The Array needed to be reset. There are TWO changed blocks, not one. This works with my sample data. If it doesn't with yours, it may be the data – Vince Jul 28 at 14:58
  • 1
    One last fix -- The vertex array can take None to separate subparts, so three lines changed – Vince Jul 28 at 17:19
  • 1
    You need to ask a new question, and include your code, with the attempt to solve the problem and a description of the issue encountered. It should only take three additional lines of code. Printing to just the console is one line of new code. – Vince Jul 31 at 13:21

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.