Using a cursor to update field in point feature class from polygon

1854
4
Jump to solution
03-13-2019 10:34 AM
Brian_McLeer
Frequent Contributor

I am working on a python script that updates a field titled 'PlanRefID' field in several point feature classes. These are populated based on a 'PlanRefID' field in a polygon feature class. The cursor will loop through and populate the 'PlanRefID' field in the point feature classes (approximately 10) correctly.

The issue I am having is that I only want it to update where the PlanRefID IS NULL in my point feature classes. However, it will loop through all records in the polygon feature class, which is taking about 1.5 - 2 hours to run. As the feature classes add more records, the processing time will only increase. I only interested in updating the point features where the PlanRefID IS NULL and referencing the polygons they intersect. 

arcpy.MakeFeatureLayer_management("Database Connections\\Server Database OWNER.sde\\Database.OWNER.FeatureDataset\\Database.OWNER.Polygon", "Database_OWNER_Polygon")  
arcpy.MakeFeatureLayer_management("Database Connections\\Server Database OWNER.sde\\Database.OWNER.FeatureDataset\\Database.OWNER.Point", "Database_Owner_Point", "PlanRefID IS NULL") 
arcpy.SelectLayerByAttribute_management("Database_Owner_Point", "NEW_SELECTION", "PlanRefID IS NULL")   

rows = arcpy.SearchCursor("Database_OWNER_Polygon") 
for row in rows: 
    arcpy.SelectLayerByAttribute_management("Database_OWNER_Polygon", "NEW_SELECTION", "\"OBJECTID\" = " + str(row.getValue("OBJECTID"))) 
    arcpy.SelectLayerByLocation_management("Database_Owner_Point", "INTERSECT", "Database_OWNER_Polygon", "", "NEW_SELECTION") 
    arcpy.CalculateField_management("Database_Owner_Point", "PlanRefID", "'{0}'".format(str(row.getValue("PlanRefID"))), "PYTHON_9.3", "") print "Finished processing " + str(row.getValue("PlanRefID"))‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍
Brian
0 Kudos
1 Solution

Accepted Solutions
MitchHolley1
MVP Regular Contributor

You could build a dictionary of both geometries and compare them with arcpy.  However, I don't know if this will be any faster... especially connecting to a non-local database.

I would test this on scratch data first.

import arcpy

#datasources
polygon = r'path\to\polygon'
point = r'path\to\point'

#cursor objects
polyCur = arcpy.da.SearchCursor(polygon, ['SHAPE@','PlanRefID'])
pointCur = arcpy.da.SearchCursor(point, ['SHAPE@','OBJECTID'],"PlanRefID IS NULL")

#dictionary to store geometry:value pairs for comparison
poly_dict = {row[0]:row[1] for row in polyCur}
point_dict = {row[0]:row[1] for row in pointCur}

#populated match dictionary of polygon.PlanRefID, if point geom is within poly geom
#see here: http://desktop.arcgis.com/en/arcmap/10.3/analyze/arcpy-classes/point.htm
match = {}
for pnt in point_dict:
    for poly in poly_dict:
        if pnt.within(poly):
            ID = point_dict[pnt]
            value = poly_dict[poly]
            match[ID] = value

#could add qry to make it run faster.. may not work with different data formats (.shp)
qry = "OBJECTID IN {}".format(tuple(k for k in match))

#iterate over points w/ qry and update PlanRefID if OBJECTID is found in dictionary
with arcpy.da.UpdateCursor(point, ['OBJECTID','PlanRefID'],qry) as cursor:
    for row in cursor:
        if row[0] in match:
            row[1] = match[row[0]]
            cursor.updateRow(row)
del cursor
            
‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

Lastly, I created a similar ESRI Script Tool that has worked successfully with ArcMap 10.4.  I call is Spatially Append Attributes.

View solution in original post

4 Replies
MitchHolley1
MVP Regular Contributor

You could build a dictionary of both geometries and compare them with arcpy.  However, I don't know if this will be any faster... especially connecting to a non-local database.

I would test this on scratch data first.

import arcpy

#datasources
polygon = r'path\to\polygon'
point = r'path\to\point'

#cursor objects
polyCur = arcpy.da.SearchCursor(polygon, ['SHAPE@','PlanRefID'])
pointCur = arcpy.da.SearchCursor(point, ['SHAPE@','OBJECTID'],"PlanRefID IS NULL")

#dictionary to store geometry:value pairs for comparison
poly_dict = {row[0]:row[1] for row in polyCur}
point_dict = {row[0]:row[1] for row in pointCur}

#populated match dictionary of polygon.PlanRefID, if point geom is within poly geom
#see here: http://desktop.arcgis.com/en/arcmap/10.3/analyze/arcpy-classes/point.htm
match = {}
for pnt in point_dict:
    for poly in poly_dict:
        if pnt.within(poly):
            ID = point_dict[pnt]
            value = poly_dict[poly]
            match[ID] = value

#could add qry to make it run faster.. may not work with different data formats (.shp)
qry = "OBJECTID IN {}".format(tuple(k for k in match))

#iterate over points w/ qry and update PlanRefID if OBJECTID is found in dictionary
with arcpy.da.UpdateCursor(point, ['OBJECTID','PlanRefID'],qry) as cursor:
    for row in cursor:
        if row[0] in match:
            row[1] = match[row[0]]
            cursor.updateRow(row)
del cursor
            
‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

Lastly, I created a similar ESRI Script Tool that has worked successfully with ArcMap 10.4.  I call is Spatially Append Attributes.

Brian_McLeer
Frequent Contributor

Thanks, Mitch,

That worked. I was able to make a separate python file for each point feature class and just schedule it sequentially in task scheduler, takes about 10 minutes to run instead of 2 hours. I also took your advice about doing the geoprocessing on my local drive. I ended up deleting the features where the PlanRefID was NULL after exporting locally then append them back into SDE at the end of the script.  Thanks for your help. 

Brian
0 Kudos
curtvprice
MVP Esteemed Contributor

Why not use Join By Location followed by a join and calculate field? I would think that would be much faster than joining feature by feature using geometry methods. I may be wrong, but if possible I like to use the tool instead to make it Esri's problem if something doesn't work. 🙂

Brian_McLeer
Frequent Contributor

I usually do that but it requires a spatial join, then a table join, then a calculate field. In the case of my schema here, I don't have a unique ID field to join back to on a table join. 

Brian