Select to view content in your preferred language

Iterate through features and select by location

2595
11
03-05-2020 03:34 PM
NataliaGutierrez1
Regular Contributor

Hello,

I have a layer with Zoning Overlays. I have a layer with parcels. I need to iterate through zoning Overlay, select the parcels falling inside, add a new field to the parcels Feature class and add some text to the selected fields, then repeat for the next zoning overlay. The loop works, insofar but it stops with a particular error that is not specific enough. 

The code runs until it has to assign the values to the second field. 

Please see below for more details.

Here's my code:

Import arcpy

# Set two geoprocessing environments
arcpy.env.workspace = r"D:\APRX, MXDS\Geo_Engine_Zoning_Project\Austin_Geo_Engine.gdb"
arcpy.env.overwriteOutput = True

# List of fields in Merged_Ovelays FC
fields = ["OBJECTID", "zoning_ove"]

# Convert austin_parcels feature class to feature layer
arcpy.MakeFeatureLayer_management("austin_parcels", "austin_parcels_layer")

# Convert Merged_Overlays feature class to feature layer
arcpy.MakeFeatureLayer_management("Merged_Overlays", "Merged_Overlays_Layer")

# arcpy.da.SearchCursor(in_table, field_names, {where_clause}, {spatial_reference}, {explode_to_points}, {sql_clause})
with arcpy.da.SearchCursor("Merged_Overlays_Layer", fields) as cursor:
    for row in cursor:
        select = "OBJECTID = {}".format(row[0])
# SelectLayerByAttribute(in_layer_or_view, {selection_type}, {where_clause}, {invert_where_clause})
        arcpy.management.SelectLayerByAttribute("Merged_Overlays_Layer", "NEW_SELECTION", select)
# SelectLayerByLocation(in_layer, {overlap_type}, {select_features}, {search_distance}, {selection_type}, {invert_spatial_relationship})
        arcpy.management.SelectLayerByLocation("austin_parcels_layer", "HAVE_THEIR_CENTER_IN", "Merged_Overlays_Layer","","NEW_SELECTION")
# Syntax: AddField(in_table, field_name, field_type, {field_precision}, {field_scale}, {field_length}, {field_alias}, {field_is_nullable}, {field_is_required}, {field_domain})
        arcpy.management.AddField('austin_parcels_layer', "Overlay_{}".format(row[1]), "TEXT")
# CalculateField(in_table, field, expression, {expression_type}, {code_block}, {field_type})
        arcpy.management.CalculateField("austin_parcels_layer", "Overlay_{}".format(row[1]), '"Overlay_{}".format(row[1])')

I am getting the following error and I am not sure how to fix it:

Traceback (most recent call last):
File "<string>", line 24, in <module>
File "c:\program files\arcgis\pro\Resources\arcpy\arcpy\management.py", line 4230, in CalculateField
raise e
File "c:\program files\arcgis\pro\Resources\arcpy\arcpy\management.py", line 4227, in CalculateField
retval = convertArcObjectToPythonObject(gp.CalculateField_management(*gp_fixargs((in_table, field, expression, expression_type, code_block), True)))
File "c:\program files\arcgis\pro\Resources\arcpy\arcpy\geoprocessing\_base.py", line 506, in <lambda>
return lambda *args: val(*gp_fixargs(args, True))
arcgisscripting.ExecuteError: ERROR 999999: Something unexpected caused the tool to fail. Contact Esri Technical Support (http://esriurl.com/support) to Report a Bug, and refer to the error help for potential solutions or workarounds.
Invalid pointer
Failed to execute (CalculateField).

Is it something with my line of code that is not working?

arcpy.management.CalculateField("austin_parcels_layer", "Overlay_{}".format(row[1]), '"Overlay_{}".format(row[1])')

My goal is to:

Copy the text from the selected feature below:

and put it into the newly created field. 

I would really appreciate your help,

Thank you so much!

Natalia

0 Kudos
11 Replies
DavidPike
MVP Frequent Contributor

For your select query I think OBJECTID should be enclosed in quotes.

select = ' " ' + "OBJECTID" + ' " '  + " = " + str(row[0])

0 Kudos
JoshuaBixby
MVP Esteemed Contributor

Although AddFieldDelimiters—ArcPy Functions | ArcGIS Desktop will still add double quotes around file geodatabase and shape file field names, they haven't been necessary for a while.  The ArcMap SQL reference for query expressions used in ArcGIS—Help | ArcGIS for Desktop (ArcMap) documentation states:

For File geodatabase data you can enclose your field names in double quotes, but it's generally not needed.

The ArcGIS Pro SQL reference for query expressions used in ArcGIS—ArcGIS Pro | ArcGIS Desktop (Pro) documentation has gone one step further and completely removed the Fields section so there are no references to field delimiters.

RandyBurton
MVP Alum

I don't think arcpy.env.workspace can take "two geoprocessing environments".  Perhaps the comma in the workspace variable is causing the "something unexpected" error.

# Set two geoprocessing environments
arcpy.env.workspace = r"D:\APRX, MXDS\Geo_Engine_Zoning_Project\Austin_Geo_Engine.gdb"
arcpy.env.overwriteOutput = True
JoshuaBixby
MVP Esteemed Contributor

Randy, I don't think the OP is trying to pass two values, the comma is in the name of the folder!  Although allowed by Windows and ArcGIS, I think most would argue commas are a bad idea in file paths.

RandyBurton
MVP Alum

I took another look at your code and was wondering if you intended to create a new field in your parcels layer for each feature in the Merged_Overlays_Layer? This could be a lot of fields to add.  Or did you intend to create a field and populate it with the name/ID of the overlay feature the parcel was located in?

For the CalculateField, I believe the single quotes should be inside the double quotes for the third parameter.

'"Overlay_{}".format(row[1])'
# try instead:
"'Overlay_{}'".format(row[1])

I was experimenting in Desktop and needed to add a 4th parameter "PYTHON_9.3" which is supposed to be optional, but without it I was getting an error.

For my testing I was using the following code in ArcMap's Python window.  Changes are probably required, but it may give you some ideas.

parcels = 'austin_parcels_layer'
pField = 'Overlay' # new field for overlay feature name/ID

overlay = 'Merged_Overlays_layer'
oFields = ['OBJECTID', 'zoning_ove']

arcpy.management.AddField(parcels, pField, 'TEXT', None, 100) # add text field with length of 100

with arcpy.da.SearchCursor(overlay,oFields) as cursor:
    for row in cursor:
        select = "OBJECTID = {}".format(row[0])
        arcpy.management.SelectLayerByAttribute(overlay, "NEW_SELECTION",select)
        arcpy.management.SelectLayerByLocation(parcels, "HAVE_THEIR_CENTER_IN", overlay, "", "NEW_SELECTION")
        arcpy.management.CalculateField(parcels, pField, "'{}'".format(row[1]), "PYTHON_9.3")
        
arcpy.management.SelectLayerByAttribute(overlay,"CLEAR_SELECTION") # clear any selections
arcpy.management.SelectLayerByAttribute(parcels,"CLEAR_SELECTION")
JoshuaBixby
MVP Esteemed Contributor

Your first Select Layer by Attribute is unnecessary and may cause issues if you are changing the selection of a layer while iterating over the layer with a Search Cursor.  Just add "SHAPE@" to your list of field names to get the shape you are interested in and drop the first Select Layer By Attribute.

Do you really want to add a new field for each loop in the Search Cursor?  Assuming austin_parcel_layer geometries can only have their center in one merged_overlays_layer, you will be creating an unnecessarily sparse matrix of columns and rows.

NataliaGutierrez1
Regular Contributor

Hi Joshua, Randy and David,

Thanks for your help!

I did need to add a new field for each loop in the Search Cursor. 

This is the code that ended up working. I am sharing it just in case anyone needs it. 

I had to fix some of the raw data for it to work correctly. I had to modify the names of the cell values to have underscores instead of spaces. 

Import arcpy

# Set two geoprocessing environments
arcpy.env.workspace = r"D:\APRX_MXDS\Geo_Engine_Zoning_Project\Austin_Geo_Engine.gdb"
arcpy.env.overwriteOutput = True

# List of fields in Merged_Ovelays FC
fields = ["OBJECTID", "zoning_ove"]

# Convert austin_parcels feature class to feature layer
arcpy.MakeFeatureLayer_management("austin_parcels", "austin_parcels_layer")

# Convert Merged_Overlays feature class to feature layer
arcpy.MakeFeatureLayer_management("Merged_Overlays", "Merged_Overlays_Layer")

# arcpy.da.SearchCursor(in_table, field_names, {where_clause}, {spatial_reference}, {explode_to_points}, {sql_clause})
with arcpy.da.SearchCursor("Merged_Overlays_Layer", fields) as cursor:
    for row in cursor:
        select = "OBJECTID = {}".format(row[0])
# SelectLayerByAttribute(in_layer_or_view, {selection_type}, {where_clause}, {invert_where_clause})
        arcpy.management.SelectLayerByAttribute("Merged_Overlays_Layer", "NEW_SELECTION", select)
# SelectLayerByLocation(in_layer, {overlap_type}, {select_features}, {search_distance}, {selection_type}, {invert_spatial_relationship})
        arcpy.management.SelectLayerByLocation("austin_parcels_layer", "HAVE_THEIR_CENTER_IN", "Merged_Overlays_Layer","","NEW_SELECTION")
# Syntax: AddField(in_table, field_name, field_type, {field_precision}, {field_scale}, {field_length}, {field_alias}, {field_is_nullable}, {field_is_required}, {field_domain})
        arcpy.management.AddField('austin_parcels_layer', "Overlay_{}".format(row[1]), "TEXT")
# CalculateField(in_table, field, expression, {expression_type}, {code_block}, {field_type})
        arcpy.management.CalculateField("austin_parcels_layer", "Overlay_{}".format(row[1]), "'Overlay_{}'".format(row[1]), "PYTHON_3")

arcpy.management.SelectLayerByAttribute("Merged_Overlays_Layer", "CLEAR_SELECTION") # clear any selections
arcpy.management.SelectLayerByAttribute("austin_parcels_layer", "CLEAR_SELECTION")
RandyBurton
MVP Alum

Thanks Joshua Bixby for the comment about using "SHAPE@".   The SelectLayerByAttribute step is not necessary.

oFields = ['OBJECTID', 'zoning_ove', 'SHAPE@']
 
with arcpy.da.SearchCursor(overlay,oFields) as cursor:
    for row in cursor:
        print "Processing feature {}".format(row[0])
        arcpy.management.SelectLayerByLocation(parcels, "HAVE_THEIR_CENTER_IN", row[2], "", "NEW_SELECTION") # row[2] is SHAPE@
        arcpy.management.CalculateField(parcels, pField, "'{}'".format(row[1]), "PYTHON_9.3")‍‍‍‍‍‍‍
JoeBorgione
MVP Emeritus

Do what all those guys suggest....

That should just about do it....
0 Kudos