Hi, I am the process of converting a geoprocessing script from ArcMap & Python 2 to Pro & Python 3 and I've run into an annoying issue with the spatial join tool. My script takes a feature layer as input and then acts on the selection in that layer. This worked fine in ArcMap but in Pro arcpy's spatial join is clearing the layer selection after it runs. I am using ArcGIS Pro 3.1. Here is an example script that demonstrates the problem:
import arcpy
def get_selection_count(in_layer):
"""return the number of selected features in the layer"""
selection = in_layer.getSelectionSet()
if selection:
return len(selection)
return 0
# envs
arcpy.env.overwriteOutput = True
arcpy.env.workspace = arcpy.env.scratchGDB
# create test feature class
fc = arcpy.management.CreateFeatureclass(arcpy.env.scratchGDB, "test_point_fc", "POINT")[0]
points = [arcpy.Point(100, 100), arcpy.Point(200, 200)]
with arcpy.da.InsertCursor(fc, ["SHAPE@"]) as crsr:
for point in points:
crsr.insertRow((point,))
# make a layer
layer = arcpy.management.MakeFeatureLayer(fc, "test_layer")[0]
# make a selection
layer = arcpy.management.SelectLayerByAttribute(layer, "NEW_SELECTION", "1=1")[0]
print(f"There are {get_selection_count(layer)} features selected.")
arcpy.SpatialJoin_analysis(layer, layer, "out_sj")
print(f"There are {get_selection_count(layer)} features selected.")
Edit:
The output would be
There are 2 features selected.
There are 0 features selected.
Is this a bug? Am I missing something? Thanks.
What were the print results?
Does the other join work?
Add Spatial Join (Data Management)—ArcGIS Pro | Documentation
albeit, it is permanent
I've edited the post to include the output. First, all 2 features are selected, then 0. I haven't tried the other tool yet. I would like to understand if this one is malfunctioning or not. Thanks.
Select Layer By Attribute (Data Management)—ArcGIS Pro | Documentation
from the python section, there is no assignment of the select layer by attributes to another variable name (line 27), perhaps that is causing the issues downstream
I can't replicate your problem, how are you running this script?
I'm running it as a standalone script, executed in VS Code through a cloned conda env from ArcGIS Pro 3.1.3, so Python 3.9.16 and arcpy version 3.1.
did you address the layer assignment thing on line 32, I am not sure that the script knows what layer is what
If I don't rebind layer to the result from SelectLayerByAttribute, then the call to getSelectionSet() will return no selection. To switch gears a bit, let me tell you a bit more about my actual project.
In my real python toolbox script, I take a feature layer as input and rely on the layer selection to process only certain features. I will try to provide the important pieces of code so you can see how it works and where it fails.
Starting out, I use a function to define the parameters:
def make_param(disp_name, p_name, data_type, default_val=None, p_type="Required", p_dir="Input"):
param = arcpy.Parameter(
displayName=disp_name,
name=p_name,
datatype=data_type,
parameterType=p_type,
direction=p_dir,
)
param.value = default_val
return param
Then, in the toolbox init, the layer parameter is defined like so:
self.parameters = [
make_param("AOI Polygon Layer", "in_lyr", "GPFeatureLayer")
]
In the tool's execute method, the argument is assigned to a local variable like so:
in_lyr = parameters[0].value
Shortly thereafter, I check for a selection and raise an exception if there is none:
# check for selection on AOI layer
selection = in_lyr.getSelectionSet()
if not selection:
arcpy.AddError("You must select AOI features to evaluate!")
raise NoSelectionError("There was no selection in the AOI layer")
Then, I open a search cursor on the layer and create a dictionary that will be used to store the results from other tools:
with arcpy.da.SearchCursor(in_lyr, ["AOI_ID"]) as cursor:
result = {row[0]: {"AOI_ID": row[0]} for row in cursor}
At this point, I run spatial join because later steps depend on its output. You will notice at the end that I've now included a line to restore the selection back to its original state after this tool is run.
# spatial join to check for overlaps in the AOI polygons if zonal stats to be used
# creating the spatial joined feature class now, otherwise it might be done repeatedly
if option_sl_score or option_gi_scores or option_canopy_cover:
arcpy.AddMessage("Checking for overlap in input AOIs ...")
arcpy.analysis.SpatialJoin(
in_lyr,
in_lyr,
"out_spatial_join",
"JOIN_ONE_TO_MANY",
match_option="WITHIN_A_DISTANCE",
search_radius=-0.1,
)
# spatial join clears layer selection, so we restore it
in_lyr.setSelectionSet(list(selection))
If line 14 above is omitted, the selection in the layer will be cleared and the code breaks downstream because I've only started gathering results for a selection of features. For example:
arcpy.TabulateIntersection_analysis(
in_zone_features=in_lyr,
zone_fields="AOI_ID",
in_class_features=wmo_bdry,
out_table="wmo_tab",
class_fields="NAME_TXT",
)
wmo_flds = ["AOI_ID", "NAME_TXT", "PERCENTAGE"]
wmo_d = {"3": "MCWD", "6": "MWMO", "7": "BCWMC", "8": "SCWMC"}
# store results in result dict
with arcpy.da.SearchCursor("wmo_tab", wmo_flds) as cursor:
for row in cursor:
aoi_id, wmo_id, perc = row
wmo = wmo_d.get(wmo_id, "OTHER")
pct_fld = f"PCT_{wmo}"
result[aoi_id][pct_fld] = round(perc, 2)
If the selection is not restored, tabulate intersection will operate on all features and the tool will fail with a KeyError exception because the aoi_id does not exist in the result dictionary. Of course, this problem can be resolved with some tweaks to the script, but my reason for posting is to either figure out if I am using the layer object from the parameter inappropriately or to determine that this is indeed a bug in arcpy.
setSelectionSet
Table—ArcGIS Pro | Documentation
This method provides an easy way to manage a table's selection. To clear the selection, use the NEW selection method with an empty list or don't set any parameters. Python lists are used for the oidList, but sets get returned from the getSelectionSet method on the Table object.
So you can confirm "selection" is > 0 (?) and you seem to have set it to a list appropriately (as per the help)