I have a python script tool that outputs points and polygons. I want to symbolize them based on a field but I'm having a hard time doing that in my code rather than manually. When I run ApplySymbologyFromLayer in the python window, the symbology applies just fine. However, when I try to do something similar at the end of my script tool, I get no errors but the symbology is unchanged. What am I missing to make this work?
Here is the code for adding the polygons. It is essentially the same for the points. I got some advice from ChatGPT while looking for a solution so I'm open to anything that needs to be remedied from that too.
polygonLayerTemplate = r"J:\User\Coding\PolygonsTemplate.lyrx"
polygonLayerName = "Polygons"
for lyr in activeMap.listLayers():
if lyr.name == polygonLayerName:
activeMap.removeLayer(lyr) # remove old version to avoid duplicates
polygonLayer = activeMap.addDataFromPath(polygonsFeatureClass)
Layer = activeMap.listLayers(polygonLayerName)[0]
arcpy.management.ApplySymbologyFromLayer(
in_layer=Layer,
in_symbology_layer=polygonLayerTemplate,
symbology_fields="VALUE_FIELD Name Name",
update_symbology="UPDATE"
)
possibly BUG-000155515 for ArcGIS Pro
Solved: Cannot Apply Symbology From Layer Using Python Scr... - Esri Community - details setting the symbology as an output parameter instead.
I've also seen it set as 'layer.symbology' as an alternative.
I've had inconsistent behavior with ApplySymbology before as well. You can sometimes coax it to work by setting the environment or saving the active project. If all else fails, You can dig into CIM and apply the symbology as a CIM update. lyrx files are just JSON definitions of CIM objects, so you can load it in and apply it like any other CIM update.
from arcpy.cim.CIMDocument import CIMLayerDocument
from arcpy.cim.cimloader.jsontocim import GetJSONTypeOBJ
from arcpy.mp import ArcGISProject
from arcpy.management import ApplySymbologyFromLayer
from arcpy import EnvManager
from arcpy.mp import Map, Layer
import json
from pathlib import Path
def lyrx_to_cim(lyrx: str) -> CIMLayerDocument:
_json = json.load(Path(lyrx).open('rt', encoding='utf-8'))
return GetJSONTypeOBJ(_json)
def get_layers(map: Map, name: str):
return (l for l in map.listLayers() if l.name == name)
def main() -> None:
data_path = r'<path>'
lyr_path = r'J:\User\Coding\PolygonsTemplate.lyrx'
lyr_cim = lyrx_to_cim(lyr_path)
lyr_name = 'Polygons'
prj = ArcGISProject('CURRENT')
for lyr in get_layers(prj.activeMap, lyr_name):
prj.activeMap.removeLayer(lyr)
prj.activeMap.addDataFromPath(data_path)
for lyr in get_layers(prj.activeMap, lyr_name):
break
else:
print(f'[ERROR] Failed to find {lyr_name}')
return
# Using a workspace Environment (with the project location as the workspace)
with EnvManager(workspace=prj.homeFolder):
ApplySymbologyFromLayer(
in_layer=lyr,
in_symbology_layer=lyr_path,
symbology_fields='VALUE_FIELD Name Name',
update_symbology='UPDATE'
)
prj.save()
# Set with CIM
lyr.setDefinition(lyr_cim)
if __name__ == '__main__':
main()
One thing you might check is your symbology_fields argument. The docs show this as a list of lists vice a string.
Tyler
Docs are sometimes inconsistent on that. There's some pretty aggressive parameter conversion that goes on with the direct API functions.
E.g. Any function that has 2 literal flags can either take a flag or a Boolean (True/False == "INVERT"/"NOT-INVERT")
If you ever get the chance, check out the fixargs decorator that wraps every function and you'll see what I mean.
@HaydenWelch, Good to know. Thank you.