When writing a python script tool, I often have GPFeatureLayer as the parameter type. These are great because then Parameter.value gives you access to most of the properties and methods of a layer as accessed through arcpy.Map.listLayers(), but infuriating because you can't access some properties. The difference is that Parameter.value is a MappingLayerObject, whatever that means, and arcpy.Map.listLayers() returns a list of arcpy.Layer objects.
So, let's say I have a python script toolbox set up something like this:
def getParameterInfo(self):
param0 = arcpy.Parameter(
name='layers',
displayName='Layers',
direction='Input',
datatype='GPFeatureLayer',
parameterType='Required',
enabled=True
)
return [param0]
def execute(self, parameters, messages):
gp_layer = parameters[0].value
layer_name = gp_layer.name
aprx = arcpy.mp.ArcGISProject('current')
active_map = aprx.activeMap
arcpy_layer = get_arcpy_layer() # ????????
sd_draft = active_map.getWebLayerSharingDraft(
'HOSTING_SERVER', 'FEATURE', layer_name, [arcpy_layer]
)
Now the context I was interested in was writing a tool to publish a bunch of layers as web layers, one by one. So that means I had to use the method arcpy.Map.getWebLayerSharingDraft(), which as one of its parameters requires an arcpy.Layer object (trying to use gp_layer will throw an error). But how do I get the right one? It's possible to give two layers the same name, so arcpy.Map.listLayers() is useless to me.
In ArcGIS Pro, in the map properties > general tab, there's a checkbox called "Allow assignment of unique numeric IDs for sharing web layers" and it seems like it's there to solve this exact problem. When you check that box, go into a layer's properties > general > LayerID, and you'll see indeed the layers have unique IDs.
How do we access those unique IDs, and thereby link every MappingLayerObject to the correct Layer? For a Layer object, you hop inside the CIM and read the property called serviceLayerID:
aprx = arcpy.mp.ArcGISProject('current')
m = aprx.activeMap
for layer in m.listLayers():
cim = layer.getDefinition('V3')
layer_id = cim.serviceLayerID
If you run dir() on a MappingLayerObject, you'll see it shares many properties and methods with a Layer object, but getDefinition() is among the missing. Instead, there is a method called GetCimJSONString(), which gives you basically the same thing as getDefinition(), but as a string.
So finally, I would write a function like the following, and call it on line 19 of my first snippet.
import json
def get_arcpy_layer(gp_feature_layer, pro_map):
gp_cim = json.loads(gp_feature_layer.GetCimJSONString())
gp_layer_id = gp_cim['serviceLayerID']
for layer in pro_map.listLayers():
cim = layer.getDefinition('V3')
layer_id = cim.serviceLayerID
if layer_id == gp_layer_id:
return layer
Thanks for reading, I hope this is helpful to somebody someday. I have struggled with this concept in many more contexts beyond just publishing web layers.
Hi there,
my answer might be a little late, but I struggled with the same issue for a long time, until I discovered the URI-property of the Layerclass. It can be accessed via the MappingLayerObject you obtain from parameter.value as well as the Layerobject you obtain through arcpy.Map.listLayers().
You can then check for a match in the URI-property of both objects which, like your code above, solves the problem with dublicates in TOC layernames.
parameterlayer = parameters[0].value # returns a MappingLayerObject
p = arcpy.mp.ArcGISProject("CURRENT")
m = p.activeMap
layers_toc = m.listLayers()
for layer in layers_toc: # returns list of Layerobjects
if layer.URI == parameterlayer.URI:
# do something