Select to view content in your preferred language

Arcpy.mapping.AddLayer selection Funk (serious ESRI devs only)

5269
44
Jump to solution
02-05-2018 11:23 AM
JosephArmbruster
New Contributor III
Hey everyone,
It's been ages since i've had to resort to the forums, but the time has come...  This is issue is occurring with:
10.3.0.4322
AND
10.5.1.7333
Problem: When a layer is added to the TOC, just as you see in the snippet below, feature selections will no longer be respected (or at least detected) by geoprocessing tools.
Description: The symptom of the problem is that obtaining the feature selection count of a geoprocessing layer in a python toolbox is inconsistent.  I've pinned it down to some issue with the way Arc (or ArcPy) is handling layers by Name.  Here are some snippets:
# make this an absolute path to some feature class in a geodatabase, you only need a minimum of two features to reproduce the issue, i'm using polygons.

path = #path to your polygon feature class with at least two features in it.
mxd = arcpy.mapping.MapDocument('CURRENT')
new_layer_name = os.path.basename(path)
arcpy.MakeFeatureLayer_management(path, new_layer_name)
new_layer = arcpy.mapping.Layer(new_layer_name)
arcpy.mapping.AddLayer(mxd.activeDataFrame, new_layer_name, 'BOTTOM')
del new_layer
del mxd
Use the code above in a geoprocessing tool to add a layer to the map.

Now, use the code below in a separate geoprocessing tool, to report the number of features selected in the map.
desc = arcpy.Describe(input_feature_class)
if desc.FIDSet:
 messages.addMessage('selection made')
 # you'll never get here with the layer above!!
else:
 messages.addMessage('no selection made')
 # you'll always get here even with a selection 😞
# count will always be the entire feature class count, since no selection was registered
# to the layer....
feature_count = int(arcpy.GetCount_management(input_feature_class).getOutput(0)

Notes:
- If you use the code above to add the feature class to the map.  Remove it from the toc (right click, remove) and add it using arc catalog (drag-drop), the feature selection will STILL not work.  Hence why I suggest it has something to do with the internal name resolution.
- If you start a new ArcMap instance, add the layer by drag-dropping it from Arc Catalog, the selection WILL work.  If you remove this layer and add it using the code above, all subsequent selections will NOT be respected.

I look forward to hearing from ESRI support.
Let me know if you have any questions,
Joseph Armbruster
0Lat Inc, Orlando Florida.
0 Kudos
44 Replies
XanderBakker
Esri Esteemed Contributor

I think Joshua Bixby  has an interesting thing there. I made some small adjustments to the python toolbox and it seems to be working now. 

# Joseph Armbruster - 0Lat Inc - www.0lat.com
# Replace FULL_PATH_TO_A_FEATURE_CLASS_IN_A_GEODATABASE with a full path to a feature class
# Load pyt into arcmap 10.3.X or 10.5.X
# Run AddFeaturesToMap
# Select one feature from the target feature layer using the select tool
# Run GetCount
# Observe the returned feature count...

import arcpy, os

class Toolbox(object):

    def __init__(self):
        self.label = "TEST FOR ESRI"
        self.alias = ""
        self.tools = [
            AddFeaturesToMap,
            GetCount
        ]

class AddFeaturesToMap(object):

    def __init__(self):
        self.label = "For ESRI Add to Map"
        self.description = ""
        self.canRunInBackground = True

    def getParameterInfo(self):
        return

    def isLicensed(self):
        return True

    def updateParameters(self, parameters):
        return

    def updateMessages(self, parameters):
        return

    def execute(self, parameters, messages):

        arcpy.env.overwriteOutput = True

        # dataset_to_process = r'FULL_PATH_TO_A_FEATURE_CLASS_IN_A_GEODATABASE'
        dataset_to_process = r'C:\GeoNet\Selection\test.gdb\myLines'

        mxd = arcpy.mapping.MapDocument('CURRENT')

        tmp_new_layer_name = "tmp layer name"
        arcpy.MakeFeatureLayer_management(dataset_to_process, tmp_new_layer_name)
        new_layer = arcpy.mapping.Layer(tmp_new_layer_name)
        new_layer.visible = True
        new_layer.name = os.path.basename(dataset_to_process)
        arcpy.mapping.AddLayer(mxd.activeDataFrame, new_layer, 'BOTTOM')
        arcpy.Delete_management(tmp_new_layer_name)

        del new_layer
        del mxd
        arcpy.RefreshTOC()

        return

class GetCount(object):

    def __init__(self):
        self.label = "For ESRI Get Count"
        self.description = ""
        self.canRunInBackground = True

    def getParameterInfo(self):

        return [
            arcpy.Parameter(
                displayName="Input Geotiles or Grids",
                name="input_geotiles_or_grids",
                datatype="GPFeatureLayer",
                parameterType="required",
                direction="Input")
        ]

    def isLicensed(self):
        return True

    def updateParameters(self, parameters):
        return

    def updateMessages(self, parameters):
        return

    def execute(self, parameters, messages):

        input_feature_class = parameters[0].valueAsText

        desc = arcpy.Describe(input_feature_class)
        fid_set = desc.FIDSet
        if fid_set == '':
            lst_fid = []
        else:
            lst_fid = fid_set.split(';')
        if len(lst_fid) > 0:
            # messages.addMessage('selection made')
            messages.addMessage('selection made: {}'.format(len(lst_fid)))
            messages.addMessage(lst_fid)
        else:
            # messages.addMessage('no selection made')
            messages.addMessage('no selection made: {}'.format(len(lst_fid)))
            messages.addMessage(lst_fid)

        feature_class_name = os.path.basename(input_feature_class)

        feature_count = int(arcpy.GetCount_management(input_feature_class).getOutput(0))
        messages.addMessage('number of features selected: %d' % (feature_count))

        return
‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍
0 Kudos
JoshuaBixby
MVP Esteemed Contributor

Although not documented, you can point Layer directly to a data set.  Try the following for the execute block:

    def execute(self, parameters, messages):

        dataset_to_process = # path to feature class

        mxd = arcpy.mapping.MapDocument('CURRENT')
        lyr = arcpy.mapping.Layer(dataset_to_process)
        arcpy.mapping.AddLayer(mxd.activeDataFrame, lyr, 'BOTTOM')
        del lyr
        del mxd
        arcpy.RefreshTOC()

        return
JosephArmbruster
New Contributor III

That's how I had it in my original script.

XanderBakker
Esri Esteemed Contributor

I just put the code provided by Joshua Bixby  in the Python toolbox and it simply works. No double layers, selection returns the correct values. 

JoshuaBixby
MVP Esteemed Contributor

So it doesn't work for you?  I am running 10.6 and it works with the tests I have run.

0 Kudos
XanderBakker
Esri Esteemed Contributor

My hands are itching to mark the Joshua's answer as the correct one, since it worked for me too (although we are both using 10.6).

0 Kudos
DanPatterson_Retired
MVP Emeritus

go for it xander_bakker

XanderBakker
Esri Esteemed Contributor

Let's just wait a moment to see if the OP can confirm the provided solution is working for him... 

0 Kudos
JosephArmbruster
New Contributor III

Correction to my previous comment, this is Not how I had it Exactly... He removed the MakeFeatureLayer management call, which was important !

0 Kudos
JohnDye2
Occasional Contributor

this was a really funny read