Quickly copy a single attribute field from multiple different polygons to a single selected point feature using SearchCurser in Arcpy.

711
8
01-25-2022 04:34 PM
BC_Admin509
New Contributor II

Hello, I am currently trying to use Arcpy to automate and autofill address attribute information on one or multiple selected points using multiple different polygon features that are also located in different GDB's. I found the following on google searches.

import arcpy
arcpy.env.overwriteOutput = True

# -------------------- Quadrant Codes --------------------
arcpy.SelectLayerByAttribute_management ("AddressMaster", "NEW_SELECTION", "STRT_NAM IS NULL")

arcpy.MakeFeatureLayer_management("I:\Workspace.gdb\Addressmaster", "lyr_address")
arcpy.MakeFeatureLayer_management("I:\Workspace.gdb\AddressQuad", "lyr_addressquad")

rows = arcpy.SearchCursor("lyr_addressquad")
for row in rows:
    arcpy.SelectLayerByAttribute_management("lyr_addressquad", "NEW_SELECTION", "\"OBJECTID \" = " + str(row.getValue("OBJECTID")))
    arcpy.SelectLayerByLocation_management("lyr_address", "INTERSECT", "lyr_addressquad", "", "NEW_SELECTION")
    arcpy.CalculateField_management("lyr_address", "Quadrant", "'{0}'".format(str(row.getValue("Quadrant"))), "PYTHON_9.3", "")

# -------------------- ZIP Codes --------------------
arcpy.SelectLayerByAttribute_management ("AddressMaster", "NEW_SELECTION", "STRT_NAM IS NULL")

arcpy.MakeFeatureLayer_management("I:\Workspace.gdb\Addressmaster", "lyr_address")
arcpy.MakeFeatureLayer_management("I:\Workspace.gdb\ZIPCode2021", "lyr_ZIPcodes")

rows = arcpy.SearchCursor("lyr_ZIPcodes")
for row in rows:
    arcpy.SelectLayerByAttribute_management("lyr_ZIPcodes", "NEW_SELECTION", "\"OBJECTID \" = " + str(row.getValue("OBJECTID")))
    arcpy.SelectLayerByLocation_management("lyr_address", "INTERSECT", "lyr_ZIPcodes", "", "NEW_SELECTION")
    arcpy.CalculateField_management("lyr_address", "ZIP", "'{0}'".format(str(row.getValue("ZCTA5CE"))), "PYTHON_9.3", "")

# -------------------- UGA Zones --------------------
arcpy.MakeFeatureLayer_management(r"I:\Workspace.gdb\Addressmaster", "lyr_address")
arcpy.MakeFeatureLayer_management(r"I:\Workspace.gdb\UGA", "lyr_UGA")

rows = arcpy.SearchCursor("lyr_UGA")
for row in rows:
    arcpy.SelectLayerByAttribute_management("lyr_UGA", "NEW_SELECTION", "\"OBJECTID \" = " + str(row.getValue("OBJECTID")))
    arcpy.SelectLayerByLocation_management("lyr_address", "INTERSECT", "lyr_UGA", "", "NEW_SELECTION")
    arcpy.CalculateField_management("lyr_address", "UGA", "'{0}'".format(str(row.getValue("Jurisdiction"))), "PYTHON_9.3", "")

# -------------------- Parcels ID --------------------
arcpy.SelectLayerByAttribute_management ("AddressMaster", "NEW_SELECTION", "STRT_NAM IS NULL")

arcpy.MakeFeatureLayer_management("I:\Workspace.gdb\Addressmaster", "lyr_address")
arcpy.MakeFeatureLayer_management("I:\Workspace.gdb\ParcelsAndAssess", "lyr_parcels")

rows = arcpy.SearchCursor("lyr_parcels")
for row in rows:
    arcpy.SelectLayerByAttribute_management("lyr_parcels", "NEW_SELECTION", "\"OBJECTID \" = " + str(row.getValue("OBJECTID")))
    arcpy.SelectLayerByLocation_management("lyr_address", "INTERSECT", "lyr_parcels", "", "NEW_SELECTION")
    arcpy.CalculateField_management("lyr_address", "PARCEL_NO", "'{0}'".format(str(row.getValue("Parcel_ID"))), "PYTHON_9.3", "")

 

This is my code so far that is currently working fine for ZIP codes, UGA zones (if combined with county polygon so there is no blanks or nulls) and Quadrant Codes, however, Parcels are a different story. When running this same code for parcels, it takes about an hour as its going row by row. I have tried using the "SUBSET_SELECTION" but it doesn't change anything. I have also tried doing a forced select by attribute at the very beginning on the points with a Null street name value (point was just created so it will always be null) still, nothing changes and it takes an hour to run.

 

Is there something I am missing that lets it use the points I have selected to spatially select the parcels so it doesn't search the whole layers database row by row and take an hour to run?

Bonus question: Is there a way I can use the SearchCurser and an else if statement for UGA so it can give a "No" for not located within a UGA

Any help would be greatly appreciated! Thank you.

Tags (3)
0 Kudos
8 Replies
GintautasKmieliauskas
Regular Contributor II

Hi,

Your problem is related with using SelectLayerByAttribute and SelectLayerByLocation. You need to use layer, not featureclass for first parameter. More info here:

https://community.esri.com/t5/python-questions/calls-to-arcpy-management-tools-get-progressively/m-p... 

https://community.esri.com/t5/python-questions/each-arcpy-loop-takes-a-bit-longer-to-finish/m-p/4333... 

0 Kudos
BC_Admin509
New Contributor II

Hello GintautasKmieliauskas,

Thank you very much for the help and reply!

I am relatively a novice at python so sorry in advance if I use the wrong concept or name for something.

I actually have the following code set up to make and call out layer features and not feature classes for all the associated features. At least, that is my understanding of how this line of code works but I may be wrong about that.

 

arcpy.MakeFeatureLayer_management(r"I:\Workspace.gdb\UGA_1", "lyr_UGA")

 

In your second example link it seems that this is the same code I am using so hopefully its correct.

In your first example link, it seems they are just giving the code a call back value. Would this be something I should do or is my SearchCurser set up in a way that it doesn't matter other then making the code a bit shorter?

 

sel_lyr = arcpy.management.SelectLayerByAttribute(fc, where_clause="ObjectID < 5")

 

 

I posted a full copy of my current script in my reply to Jeff if you would like a reference.

0 Kudos
JeffK
by MVP Regular Contributor
MVP Regular Contributor

Edited to fix parameter positions in the example code.

 

You have some duplicated processes going on there. You only need to create the address FeatureLayer once:

 

arcpy.SelectLayerByAttribute_management ("AddressMaster", "NEW_SELECTION", "STRT_NAM IS NULL")
arcpy.MakeFeatureLayer_management(r"I:\Workspace.gdb\Addressmaster", "lyr_address")

 

 

Use the arcpy.da.SearchCursor instead of the old cursor.

For each row in parcels (and the others that you are doing), you are creating a selection again for that object id/ row. You can just pass the geometry of that row to SelectLayerByLocation_management method instead:

 

with arcpy.da.SearchCursor("lyr_parcels", ['SHAPE@', 'Parcel_ID']) as sCur:
    for row in sCur:
        select_geometry = row[0]
        selected = arcpy.SelectLayerByLocation_management("lyr_address", "INTERSECT", select_geometry, "", "NEW_SELECTION")
        arcpy.CalculateField_management(selected, "PARCEL_NO", "'{0}'".format(str(row[1])), "PYTHON_9.3", "")

 

 

Bonus answer: Yes.  Add a conditional to check for counts of selected.

 

with arcpy.da.SearchCursor("lyr_UGA", ['SHAPE@', 'Jurisdiction']) as sCur:
    for row in sCur:
        select_geometry = row[0]
        selected = arcpy.SelectLayerByLocation_management("lyr_UGA", "INTERSECT", select_geometry, "", "NEW_SELECTION")
        
        # check if there was a selection and assign the parcel_id or No
        val = str(row[1]) if int(arcpy.GetCount_management(selected).getOutput(0))> 0 else 'No'
        
        arcpy.CalculateField_management(selected, "UGA", "'{0}'".format(val), "PYTHON_9.3", "")
        

 

 

These are not tested so there may be some tweaking that you will need to do.

0 Kudos
BC_Admin509
New Contributor II

Hello Jeff,

Thank so much for the quick reply!

 

import arcpy
arcpy.env.overwriteOutput = True
arcpy.env.workspace = r"I:\Workspace.gdb"

arcpy.MakeFeatureLayer_management (r"I:\Workspace.gdb\AddressMaster", "lyr_address")

arcpy.SelectLayerByAttribute_management ("AddressMaster", "NEW_SELECTION", "STRT_NAM IS NULL")

# -------------------- Quadrant Codes --------------------
arcpy.SelectLayerByAttribute_management ("AddressMaster", "NEW_SELECTION", "STRT_NAM IS NULL")

arcpy.MakeFeatureLayer_management(r"I:\Workspace.gdb\AddressQuad", "lyr_addressquad")

rows = arcpy.SearchCursor("lyr_addressquad")
for row in rows:
    arcpy.SelectLayerByAttribute_management("lyr_addressquad", "NEW_SELECTION", "\"OBJECTID \" = " + str(row.getValue("OBJECTID")))
    arcpy.SelectLayerByLocation_management("lyr_address", "INTERSECT", "lyr_addressquad", "", "NEW_SELECTION")
    arcpy.CalculateField_management("lyr_address", "Quadrant", "'{0}'".format(str(row.getValue("Quadrant"))), "PYTHON_9.3", "")

# -------------------- ZIP Codes --------------------
arcpy.MakeFeatureLayer_management(r"I:\Workspace.gdb\ZIPCode2021", "lyr_ZIPcodes")

rows = arcpy.SearchCursor("lyr_ZIPcodes")
for row in rows:
    arcpy.SelectLayerByAttribute_management("lyr_ZIPcodes", "NEW_SELECTION", "\"OBJECTID \" = " + str(row.getValue("OBJECTID")))
    arcpy.SelectLayerByLocation_management("lyr_address", "INTERSECT", "lyr_ZIPcodes", "", "NEW_SELECTION")
    arcpy.CalculateField_management("lyr_address", "ZIP", "'{0}'".format(str(row.getValue("ZCTA5CE"))), "PYTHON_9.3", "")

# -------------------- UGA Areas --------------------
arcpy.MakeFeatureLayer_management(r"I:\Workspace.gdb\UGA", "lyr_UGA")
with arcpy.da.SearchCursor("lyr_UGA", ['SHAPE@', 'Jurisdiction']) as sCur:
    for row in sCur:
        select_geometry = row[0]
        selected = arcpy.SelectLayerByLocation_management("lyr_UGA", "INTERSECT", "lyr_address", "", select_geometry, "NEW_SELECTION")
        
        # check if there was a selection and assign the parcel_id or No
        val = str(row[1]) if int(arcpy.GetCount_management(selected).getOutput(0))> 0 else 'No'
        
        arcpy.CalculateField_management(selected, "UGA", "'{0}'".format(val), "PYTHON_9.3", "")

# -------------------- Parcel ID --------------------
arcpy.MakeFeatureLayer_management("I:\Workspace.gdb\ParcelsAndAssess", "lyr_parcels")

arcpy.SelectLayerByAttribute_management ("AddressMaster", "NEW_SELECTION", '"STRT_NAM" is Null')

with arcpy.da.SearchCursor(Parcel, ['SHAPE@', 'ParcelID']) as sCur:
    for row in sCur:
        select_geometry = row[0]
        selected = arcpy.SelectLayerByLocation_management("lyr_address", "INTERSECT", "lyr_parcels", "1", select_geometry, "NEW_SELECTION")
        arcpy.CalculateField_management(selected, "Parcel_ID", "'{0}'".format(str(row[1])), "PYTHON_9.3", "")

 

 

There seems to be two things possibly causing this. When I removed the .da. from rows = arcpy.SearchCursor from Quadrant and Zip, those codes began working normally. The other is when I try to run the Parcel selection code or UGA with select_geometry, it causes the following error. The moment I remove that, it runs again but back to being really slow or as stated below for UGA.

arcgisscripting.ExecuteError: ERROR 000622: Failed to execute (Select Layer By Location). Parameters are not valid.
ERROR 000800: The value is not a member of INVERT | NOT_INVERT.

For UGA, I moved the select_geometry over as seen below but then it was pulling the wrong UGA zone but at least this time it ran without erroring.

 

with arcpy.da.SearchCursor("lyr_UGA", ['SHAPE@', 'Jurisdiction']) as sCur:
    for row in sCur:
        select_geometry = row[0]
        arcpy.SelectLayerByLocation_management(select_geometry, "INTERSECT", "lyr_address", "", "NEW_SELECTION")
        val = str(row[1]) if int(arcpy.GetCount_management(selected).getOutput(0))> 0 else 'No'
        
        arcpy.CalculateField_management("lyr_address", "UGA", "'{0}'".format(val), "PYTHON_9.3", "")

 

 

I feel like its really close and I am just horrible with python and missing something.

0 Kudos
JeffK
by MVP Regular Contributor
MVP Regular Contributor

Looks like I had the select_geometry in the wrong parameter spot and the code you posted has an additional '1' in it, which is offsetting the positional parameters and causing the ERROR 000622. Try this:

 

selected = arcpy.SelectLayerByLocation_management("lyr_address", "INTERSECT", select_geometry, "", "NEW_SELECTION"

 

0 Kudos
BC_Admin509
New Contributor II

Awesome! This fixed the that error but created another error.

ERROR 000622: Failed to execute (Select Layer By Location). Parameters are not valid.
ERROR 000628: Cannot set input into parameter search_distance

Once I removed the "lyr_address" callout before select_geometry it began working like normal however, sadly now its still going through our whole parcel layer (2+ hours). The UGA is running as well but still grabbing the wrong UGA zone and seems to not see there is no parcel intersecting the points. The UGA it is grabbing is the last 2 in the attributes of  the 71 areas.

 

#old
selected = arcpy.SelectLayerByLocation_management("lyr_UGA", "INTERSECT", "lyr_address", select_geometry, "", "NEW_SELECTION")

#new
selected = arcpy.SelectLayerByLocation_management("lyr_UGA", "INTERSECT", select_geometry, "", "NEW_SELECTION")

 

 

When tested on 2 points with 1 inside a UGA area, it is returning a Null value for both with no errors but also selecting selecting the wrong UGA areas still but more of them.

0 Kudos
JeffK
by MVP Regular Contributor
MVP Regular Contributor

You might try 'WITHIN' instead of intersect for the UGA.  The position of the layers/parameters matters, and you can almost read it like a sentence to make sure that you have the right order/ operation.

the first position, (lyr_address) = The selection will be applied to these features.

overlap_type ('INTERSECT', 'WITHIN', 'CONTAINS', etc) = how they will be evaluated

select_features = the features used to make the selection.  So if Address is points, it reads:

lyr_address WITHIN UGA (shape).

When you move lyr_UGA in the first position, it is just selecting itself and not anything in the lyr_Address, and that may have been my fault mixing those up in the code above, intersect may still work if you correct that...

with arcpy.da.SearchCursor("lyr_UGA", ['SHAPE@', 'Jurisdiction']) as sCur:
    for row in sCur:
        select_geometry = row[0]
        selected = arcpy.SelectLayerByLocation_management("lyr_Address", "WITHIN", select_geometry, "", "NEW_SELECTION")
        
        # check if there was a selection and assign the parcel_id or No
        val = str(row[1]) if int(arcpy.GetCount_management(selected).getOutput(0))> 0 else 'No'
        
        arcpy.CalculateField_management(selected, "UGA", "'{0}'".format(val), "PYTHON_9.3", "")
        

 

BC_Admin509
New Contributor II

Thank you very much again for the help Jeff! This worked and UGA is running smoothly now. Trying it on parcels now but wont know the answer for another 2+ hours. I may just look at adding parcels as a manual entry in the final code when the point is created and the tool is ran.

0 Kudos