I have a script that needs to:
Current attempts result in broken layers. Here's my current approach:
def _ensure_site_layer(map_obj, site_fc_path, layer_name=SITE_LAYER_NAME):
"""Add site boundary layer to map"""
try:
# Remove any existing site boundary layers
for lyr in list(map_obj.listLayers()):
if lyr.name in (layer_name, "SiteBoundary_Export", "HE_SiteBoundary"):
map_obj.removeLayer(lyr)
# Trying to add layer file and update its source
SPECIFIC_LF = r"lyrx source removed"
lf = arcpy.mp.LayerFile(SPECIFIC_LF)
map_obj.addLayer(lf, "TOP")
# Need help with this part - updating the source
# site_fc_path contains the input parameter path
return added_layer
except Exception as e:
arcpy.AddWarning(f"_ensure_site_layer failed: {str(e)}")
return None
How the Function is Called:
# Example of how this function is used in the script
prof_map = _add_layers_to_profile_map(site_fc_path, tran_fc_path)
he_map = _he_add_layers_to_map(site_fc_path, he_buffer_fc_path, he_layer_paths)
# etc...
Does anyone know how i can get this script to load the lyrx correctly and change its source?
You can directly update the CIM using the getDefinition and setDefinition options, I tend to have more luck with that. Once the layer is added, you can access it using the map definition directly.
You can also use the updateConnectionProperties method that's exposed by arcpy.mp for Layer objects. You'll need to also use the connectionProperties attribute to get the current connection. This method basically does the same thing as the CIM option, but has some added checks in place.
def _ensure_site_layer(map_obj: arcpy.mp.Map, site_fc_path: str, layer_name: str=SITE_LAYER_NAME):
"""Add site boundary layer to map"""
try:
# Remove any existing site boundary layers
for lyr in list(map_obj.listLayers()):
if lyr.name in (layer_name, "SiteBoundary_Export", "HE_SiteBoundary"):
map_obj.removeLayer(lyr)
# Trying to add layer file and update its source
SPECIFIC_LF = r"lyrx source removed"
lf = arcpy.mp.LayerFile(SPECIFIC_LF)
# Docs say that addLayer returns a list of layers,
# if you are adding a group layer from the LYRX, there
# will be GroupLayer + sublayer layers here I believe
#
# The URI is the assigned CIMPATH of the layer.
# If you unzip an aprx, you can see the CIMPATH is
# just a relative filepath to the layer/lyrx definition
# in the zipped directory from the root
added_layer = map_obj.addLayer(lf, "TOP").pop() # .pop() grabs first layer
path_to_gdb = r'C:\Path\To\GDB.gdb'
USE_CIM = True
if USE_CIM:
lyr_cim = added_layer.getDefinition('V3')
# This example re-paths to a GDB (Relative paths are valid)
# If you want to change the workspace type, you'll need to also
# update the dataConnection.workspaceFactory string to the correct magic
# type string
# Here's an example of the CIM for dataConnection
# "dataConnection": {
# "featureDataset": "<FEATURE_DATASET>"|null,
# "workspaceConnectionString": "DATABASE=C:\\Path\\To\\GDB.gdb",
# "workspaceFactory": "FileGDB",
# "dataset": "<FC_NAME>",
# "datasetType": "esriDTFeatureClass",
# "customParameters": [],
# "type": "CIMFeatureDatasetDataConnection"
# },
lyr_cim.dataConnection.workspaceConnectionString = f'DATABASE={path_to_gdb}'
added_layer.setDefinition(lyr_cim)
else:
# You can also try using the builtin updateConnectionProperties and connectionProperties
new_conn = added_layer.connectionProperties
new_conn['connection_info']['database'] = path_to_gdb
added_layer.updateConnectionProperties(added_layer.connectionProperties, new_conn)
return added_layer
except Exception as e:
arcpy.AddWarning(f"_ensure_site_layer failed: {str(e)}")
return None
At the end of the day, almost everything in the mapping module is just editing the JSON definition of layers in an aprx or on a webservice. The CIM is just raw access to those definitions. aprx, mapx, and lyrx files can all be opened directly in a text editor and read as json data. The only have a small CIMLayerDocument wrapper around the CIM for the contained objects that tells Arc how to structure the contained layers:
Think I'd be tempted to work it the other way
Add the layers you want to the map and then use the ApplySymbologyFromLayer tool, pointed at your layer file to update the symbology
I second this approach and it is one I employ. Add the layers and update symbololgy from the .lyrx library. If symbology changes, update the .lyrx and apply across the layers in multiple maps as required.