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.