Iterate features and select by location

6622
5
Jump to solution
11-13-2018 07:31 PM
JeffThomasILM
Occasional Contributor II

I've seen many similar questions but somehow never found an answer that I can understand and apply to my problem. I have a layer with Census tracts. I have a layer with parcels. I need to iterate through each tract, select the parcels falling inside, do some things to the parcels (for the sake of testing, I'm trying to copy the parcels in each tract into a new feature class named with the tract number), then repeat for the next tract. The loop works, insofar as it runs through each tract and creates a new feature class that is named with the tract number. But rather than each feature class containing only the parcels falling inside the respective tract, each resulting feature class includes all parcels falling inside all tracts. I'm clearly missing some important construct in my loop. Please help!

with arcpy.da.SearchCursor("tracts", "TRACT_NUMBER") as cursor:
  for row in cursor:
    arcpy.SelectLayerByLocation_management("parcels","HAVE_THEIR_CENTER_IN","tracts")‍‍‍‍‍‍‍‍‍
    arcpy.CopyFeatures_management("parcels", "parcels_"+"{}".format(row[0]))‍‍‍‍‍‍‍‍
1 Solution

Accepted Solutions
JoeBorgione
MVP Emeritus

I did something similar a year or so ago and the basic logic was this (adjusted for your use): Loop through your tracts and select a tract as you go.  Then you can select the parcels within that selected tract and do your mojo. Step to the next tract, make a new selection for it, and then a new spatial selection etc.  

fields = ['OBJECTID', 'TRACT_NUMBER']
arcpy.MakeFeatureLayer('tracts','tractsLyr')
arcpy.MakeFeatureLayer('parcels', 'parcelsLyr')
with arcpy.da.SearchCursor('tractsLyr', fields) as cursor:
  for row in cursor:
    select = 'OBJECTID = {}'.format(row[0])        
    arcpy.SelectLayerByAttribute_management('tractsLyr','NEW_SELECTION',select)
    arcpy.SelectLayerByLocation_management('parcelsLyr','HAVE_THIER_CENTER_IN',
    'tractsLyr,' ','NEW_SLECTION')
    arcpy.CopyFeatures_management("parcelsLyr", 'parcels_{}'.format(row[1]))‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

 This is untested so buyer beware.....

That should just about do it....

View solution in original post

5 Replies
NeilAyres
MVP Alum

I think you will have to create a layer first, before the select by location.

Make Feature Layer—Help | ArcGIS Desktop 

DuncanHornby
MVP Notable Contributor

You are not actually sub-setting your tracts layer in anyway, you have looped over them but done nothing with them. You can either make a temporary layer as Neil Ayres‌ suggests or select the tract in question using a select by attribute tool. As geo-processing always honour selections your select by location would behave as expected. You select parcels that full within the selected tract; as you have it now you are selecting parcels that full within all tracts as you have not actually sub-setted them in anyway.

On a side note I would also get into the habit of specify all the parameters of the tools as it makes debugging easier. For example what selection type is your select by location using?

0 Kudos
JoeBorgione
MVP Emeritus

I did something similar a year or so ago and the basic logic was this (adjusted for your use): Loop through your tracts and select a tract as you go.  Then you can select the parcels within that selected tract and do your mojo. Step to the next tract, make a new selection for it, and then a new spatial selection etc.  

fields = ['OBJECTID', 'TRACT_NUMBER']
arcpy.MakeFeatureLayer('tracts','tractsLyr')
arcpy.MakeFeatureLayer('parcels', 'parcelsLyr')
with arcpy.da.SearchCursor('tractsLyr', fields) as cursor:
  for row in cursor:
    select = 'OBJECTID = {}'.format(row[0])        
    arcpy.SelectLayerByAttribute_management('tractsLyr','NEW_SELECTION',select)
    arcpy.SelectLayerByLocation_management('parcelsLyr','HAVE_THIER_CENTER_IN',
    'tractsLyr,' ','NEW_SLECTION')
    arcpy.CopyFeatures_management("parcelsLyr", 'parcels_{}'.format(row[1]))‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

 This is untested so buyer beware.....

That should just about do it....
NataliaGutierrez1
New Contributor III

Hi Joe,

I am dealing with something similar to the original question from Jeff Thomas.

The difference is that instead of copying the selected features, I need to create a new field and add some values to those features.

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
JoeBorgione
MVP Emeritus

Natalia-  I've got one foot out the door, but if you would be so kind as to copy your code and paste it into the Syntax Highlighter, I'll take a look at it first thing in the morning.  I'm on Mountain Standard Time in the USA...

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