Cannot Apply Symbology From Layer Using Python Script

6637
12
Jump to solution
04-03-2020 01:56 PM
VinceE
by
Occasional Contributor II

I am struggling to apply symbology from an existing layer file to an output from a script tool. The below snippet is from a larger script tool that takes a DEM, clips it to the Map View, smooths it using Focal Statistics, creates a feature class of contours in an existing geodatabase, and then adds an index field. The contour feature class is automatically added to the map (seemingly regardless of my use of env.addOutputsToMap). Seems like the use of either "Add Field" or "Calculate Field" does this, which is fine, I want it there anyway.

out_gdb = r"F:\ContourTest\ContourTest.gdb"
cont_file = fr"{out_gdb}\Contours_20"

current_project = arcpy.mp.ArcGISProject("CURRENT")
current_map = current_project.listMaps()[0]
current_map.addDataFromPath(fr"{cont_file}")

cont_layer = current_map.listLayers("Contour*")[0]
symbology = r"F:\ContourTest\Contour_Index.lyrx"

arcpy.management.ApplySymbologyFromLayer(in_layer=cont_layer,
    in_symbology_layer=symbology,
    symbology_fields="VALUE_FIELD Index_Contour Index_Contour",
    update_symbology="MAINTAIN")‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

The above code is the last bit of the larger script. The whole thing, including this bit, runs without exceptions, but the symbology does not apply as expected.

But, if I then take this bit of code and paste it exactly into the Python Window and run it, another copy of the contour feature class is added to the TOC, and the correct symbology is applied. Works perfectly. If I then remove both, the symbolized contours and the unsymbolized contours, and run the code above in the Python Window again, just a symbolized version is added (or an unsymbolized version is added, and then the symbology is applied, either way). Again, works perfectly.

I have tried using versions of MakeFeatureLayer, addLayer, all kinds of things--I am likely getting tripped up over the colloquial use of the term "layer," vs. the technical meaning of layer, layer file, layer object in code vs path to layer, etc. etc.

Happy to paste the entire script if that is helpful, just didn't want to clutter the question. This a script tool in a regular toolbox (.tbx) with parameters and some basic validation set up within Pro. The validation pertains to whether a contour index field should be created, checking for integer inputs, etc., so I don't think it should interfere here, but who knows. I am running ArcGIS Pro 2.5.0--it tells me that this is the current version.

1 Solution

Accepted Solutions
DrewFlater
Esri Regular Contributor

You do not need to use arcpy mp or ApplySymbology to symbolize an output dataset of a script tool.

There are three approaches:

1. Open the script tool properties, go to the Parameters page, find the output feature class parameter, scroll right to the Symbology column  click in the cell and then browse to your layer file. This is the easiest for always symbolizing the output the same way.

2. If you need to symbolize differently depending on the output shape type or some other factors you may want to write code to symbolize the output. You have access to a parameter's same Symbology property set through the UI above available in tool validation. See here: Setting output symbology in scripts—Geoprocessing and Python | Documentation 

3. There is a new arcpy function, arcpy.SetParameterSymbology also exactly for this purpose. This allows you to keep the Symbology logic in your source code instead of validation or the tool properties.

Arcpy mp is for mapping workflow automation not GP output symbolization. These other methods give you a ton of options for performing the latter.

View solution in original post

12 Replies
DrewFlater
Esri Regular Contributor

You do not need to use arcpy mp or ApplySymbology to symbolize an output dataset of a script tool.

There are three approaches:

1. Open the script tool properties, go to the Parameters page, find the output feature class parameter, scroll right to the Symbology column  click in the cell and then browse to your layer file. This is the easiest for always symbolizing the output the same way.

2. If you need to symbolize differently depending on the output shape type or some other factors you may want to write code to symbolize the output. You have access to a parameter's same Symbology property set through the UI above available in tool validation. See here: Setting output symbology in scripts—Geoprocessing and Python | Documentation 

3. There is a new arcpy function, arcpy.SetParameterSymbology also exactly for this purpose. This allows you to keep the Symbology logic in your source code instead of validation or the tool properties.

Arcpy mp is for mapping workflow automation not GP output symbolization. These other methods give you a ton of options for performing the latter.

VinceE
by
Occasional Contributor II

Drew,

Thanks for the helpful response, it appears I was approaching the problem from the wrong direction. Your last line in particular about the intended purpose of arcpy mp clears things up for me. Sounds like one of that above options will do exactly what I need.

VinceE
by
Occasional Contributor II

I still haven't been able to figure this out. All of the above solutions seem to hinge on the output feature class being defined as a tool parameter, which is not applicable in my case. The UI takes inputs and an output location--the name of the output is generated within the script, not provided by the user. The below is the same as my first post (I believe), just cleaned up a bit.

current_project = arcpy.mp.ArcGISProject("CURRENT")
map = current_project.listMaps()[0]

cont_file = r"FULLPATH\Contours_2"

cont_layer = map.addDataFromPath(cont_file)

cont_symbol = r"FULLPATH\Index_Contour_Symbology.lyrx"

arcpy.management.ApplySymbologyFromLayer(cont_layer, cont_symbol)

The above is the final function in my python script. It adds the layer, but it does not apply the symbology. When the exact same block is run from the python window, it works as expected--it adds the existing feature class as a layer, and applies the symbology from the predefined .lyrx file. For clarity, FULLPATH in the snippet above is replaced with the actual path in the code.

It's not clear to me why this is considered "GP output symbolization" and not "mapping workflow automation." If this is not an applicable use case, then when would someone opt to use ApplySymbologyFromLayer?

0 Kudos
GeoNZ
by
Occasional Contributor

I don't consider this to be a solution as it doesn't work for mapping workflow automation which is still bugged. In the version of Pro I'm using now (2.6.1), arcpy.management.ApplySymbologyFromLayer() does work for Maps, but not for Scenes and not when using the 'Current' for either maps OR scenes (i.e. only works when referencing an ArcPro project that is not open in the current session).

 

 

LeandraGordon
Occasional Contributor II

For Python Toolboxes: Defining parameters in a Python toolbox—Geoprocessing and Python | Documentation 

which helps as a workaround for this (Bug BUG-000119907) (I hope!)

0 Kudos
VinceE
by
Occasional Contributor II

If this is just a bug, that's fine, I'll obviously have to figure out a workaround. I'm still trying to figure out if that is in fact the case, or if I'm improperly using these functions. Thanks for the link.

MattGKL
New Contributor

It has been a while since this thread and similar ones have been posted, and I have not seen a straightforwad workaround being shared out in the wild yet, so here's my "if its works, it's not stupid" solution:

 

 

# apply symbology from a lyrx file so that the lines are red and visible for user selection
# to note: symbology does not get updated for layers already loaded, 
# but will be applied subsequently to layers created from the layer with applied symbology using arcpy.analysis.Select()
orig_lyr = current_map.addDataFromPath(os.path.join(arcpy.env.workspace,"orig_lyr"))
arcpy.management.ApplySymbologyFromLayer(orig_lyr, os.path.join(os.path.dirname(arcpy.env.workspace), "layerfile.lyrx"))
new_lyr = os.path.join(arcpy.env.workspace,"new_lyr")
arcpy.analysis.Select(orig_lyr,
                      new_lyr,
                      "")
current_map.addDataFromPath(new_lyr)
# delete the original layer
arcpy.management.Delete(current_map.listLayers()[1])

 

 

Hopefully this helps someone, especially since this bug has been marked as "Will not be addressed" by Esri.

MattHowe
Occasional Contributor

I can't even seem to get this to work. It's madness as to why this isn't possible. I have a layer that I create in a custom script that isn't a parameter and I can't do something simple like apply symbology. Grrr...

MattGKL
New Contributor

You might want to try creating both feature classes in a local gdb since that is what I was working with. Otherwise, my go-to now is to adjust the layer CIM directly like here https://gis.stackexchange.com/questions/338301/what-is-arcpy-cim

0 Kudos