Select to view content in your preferred language

ApplySymbologyFromLayer GeoProcessing Tool

5012
30
Jump to solution
06-19-2019 11:09 AM
BrianBulla
Honored Contributor

Hi,

I'm trying to update the layers of a map by running the ApplySymbologyFromLayer GeoProcessing tool through some .NET code.  For a while, there was a reported BUG-000106281 about this, but it is apparently fixed in 2.3.

But.....I still cannot get it to work.  Is there something wrong with my code?  All of my .lyr files are located on a network drive.  I get a NULL Reference Exception at the .ExecuteToolAsync line.

Thanks,

                Map map = MapView.Active.Map;

                pBar.Minimum = 0;
                pBar.Maximum = map.Layers.Count;

                this.Cursor = Cursors.Wait;

                GPExecuteToolFlags flags = GPExecuteToolFlags.GPThread;

                foreach (Layer layer in map.GetLayersAsFlattenedList().OfType<FeatureLayer>())
                {   
                    var gpresult = await Geoprocessing.ExecuteToolAsync("ApplySymbologyFromLayer_management", new string[] { layer.Name.ToString(), @"K:\DSM Shared\ArcMap Symbology\10.2 Schema\Editing Symbols\" + layer.Name.ToString() + ".lyr" }, null, null, flags);
                    pBar.Value = pBar.Value + 1;
                }‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍
Tags (2)
0 Kudos
30 Replies
BrianBulla
Honored Contributor

Hi Nobbir Ahmed‌,

I've modified your code to work with my filepaths and created a working .lyrx, but it makes no difference.  Still the symbology does not apply.  Also, for me the map.Redraw is not available.  I am at 2.3.3.  Are you using a different SDK??

Also, as Sean Jones‌ mentioned, I have tried this with and without spaces in my filepath, but it doesn't seem to make any difference.

0 Kudos
by Anonymous User
Not applicable

Hey Brian,

I'll pass it over to Nobbir from here.

I did some testing yesterday however and observed the following.

If the inputs have no spaces, then it seems to work all the time. (note that you cant find GP examples with spaces...)

With spaces in the paths (such as your example) then I got mixed results.

   Old map with an old layer, i couldn't get it to work at all with just the command name and mva.

   New map with a new layer then it works fine, even if the inputs have spaces. This concurs with Gintautas's comments above.

   Putting the GPThread flag in the end also works for all scenarios, with the data I was using. Old or new. This kind of agrees with your last tests although you noticed it works some of the time.

BrianBulla
Honored Contributor

Hi Nobbir Ahmed‌,

Another thing I am noticing is that when the symbology does take, it doesn't always show up right.  After manually doing some testing, it seems like it's the optionsal "Update Symbology Ranges by Data" field that needs to be modified to "MAINTAIN" in order for the symbology to show up properly.

I've added it to my Value Array, but when doing that it still only works once in a while.

var mva = Geoprocessing.MakeValueArray(layer.Name.ToString(), @"K:\DSM Shared\ArcMap Symbology\TEST\Editing Symbols\" + layer.Name.ToString() + ".lyrx", null, "MAINTAIN");‍‍

Not sure if that helps or just makes things more confusing.

Is there another way to accomplish updating the symbology without using the GP Tool??  In my ArcObjects code for ArcMap I do this same thing using the following code.  Is there an equivalent method to this I should look at??

Do Until pLayer Is Nothing 'loop through all the layers
                
                'create an object to refence GxLayer, an ArcCatalog layer
                Dim gxLayer As IGxLayer = New GxLayerClass
                Dim gxFile As IGxFile = CType(gxLayer, IGxFile)

                If btnMapBooks.Checked = True Then
                    gxFile.Path = "K:\DSM Shared\ArcMap Symbology\10.2 Schema\Mapbook Symbols\" & pLayer.Name & ".lyr"
                ElseIf btnEditing.Checked = True Then
                    gxFile.Path = "K:\DSM Shared\ArcMap Symbology\10.2 Schema\Editing Symbols\" & pLayer.Name & ".lyr"
                ElseIf btnVLS.Checked = True Then
                    gxFile.Path = "K:\DSM Shared\ArcMap Symbology\10.2 Schema\VLS_Layers\" & pLayer.Name & ".lyr"
                End If


                If Dir$(gxFile.Path) <> "" Then
                    'if the .lyr file exists continue, else skip
                    Dim pGeoLayer As IGeoFeatureLayer
                    pGeoLayer = gxLayer.Layer
                    Dim pAnnoLayerPropsColl As IAnnotateLayerPropertiesCollection
                    pAnnoLayerPropsColl = pGeoLayer.AnnotationProperties

                    pLayer.Renderer = pGeoLayer.Renderer
                End If

                bCanceled = Not pTC.Continue

                'show a message box if the user hits ESC
                If bCanceled Then
                    MsgBox("Operation Cancelled.  Some layers may have been changed.", MsgBoxStyle.Critical, "User Interrupt")
                    Me.Cursor = Windows.Forms.Cursors.Arrow
                    Me.Close()
                    Exit Sub
                End If

                'go to the next layer
                pLayer = pEnumLayer.Next

                'increase the Progress Bar after each layer is processed
                pBar.Value = pBar.Value + 1

            Loop‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍
0 Kudos
UmaHarano
Esri Regular Contributor

Hi Brian

Pro 2.4 allows you to apply symbology from a layer file using the Pro .NET API.  A new LayerDocument class at 2.4 provides this functionality. 

Pro 2.4 will be released this week. Using Pro 2.4, the snippet below will apply symbology from a lyr file to a feature layer in the TOC.

private static async Task ModifyLayerSymbologyFromLyrFileAsync(IEnumerable<FeatureLayer> featureLayers, string layerFile)
        {
            await QueuedTask.Run( () => {
                foreach (var featureLayer in featureLayers)
                {
                    //Get the Layer Document from the lyrx file
                    var lyrDocFromLyrxFile = new LayerDocument(layerFile);
                    var cimLyrDoc = lyrDocFromLyrxFile.GetCIMLayerDocument();

                    //Get the renderer from the layer file
                    //This lyr file has a unique value renderer.
                    var rendererFromLayerFile = ((CIMFeatureLayer)cimLyrDoc.LayerDefinitions[0]).Renderer as CIMUniqueValueRenderer;

                    //Apply the renderer to the feature layer
                    featureLayer?.SetRenderer(rendererFromLayerFile);
                }
               
            });
        }‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

Thanks

Uma

BrianBulla
Honored Contributor

Sounds good.  I will keep an eye out for the 2.4 update.  Thanks for letting me know.  I've been working with Nobbir to get the GP Tool code to work, but I'll check out this new class once it's available.

0 Kudos
BrianBulla
Honored Contributor

Uma Harano‌,

You are your team at esri are genius'!!  This works really well and is way faster than using the GP tool  Thanks so much for the sample code.  I only had to make a slight modification to it, and it works perfectly.

Gintautas Kmieliauskas‌, check it out.  

using ArcGIS.Desktop.Framework.Controls;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using ArcGIS.Desktop.Mapping;
using ArcGIS.Desktop.Catalog;
using ArcGIS.Desktop.Framework.Threading.Tasks;
using ArcGIS.Desktop.Core.Geoprocessing;
using ArcGIS.Core.CIM;

namespace SymbologyTool_ArcPro
{
    /// <summary>
    /// Interaction logic for wdwSymbology.xaml
    /// </summary>
    public partial class wdwSymbology : ProWindow
    {
        public wdwSymbology()
        {
            InitializeComponent();
        }

        private static async Task ModifyLayerSymbologyFromLyrFileAsync(IEnumerable<FeatureLayer> featureLayers)
        {
            await QueuedTask.Run(() => {
                foreach (var featureLayer in featureLayers)
                {
                    //Get the Layer Document from the lyrx file
                    var lyrDocFromLyrxFile = new LayerDocument(@"K:\DSM Shared\ArcMap Symbology\TEST\Editing Symbols\" + featureLayer.Name.ToString() + ".lyrx");
                    var cimLyrDoc = lyrDocFromLyrxFile.GetCIMLayerDocument();

                    //Get the renderer from the layer file
                    //This lyr file has a unique value renderer.
                    var rendererFromLayerFile = ((CIMFeatureLayer)cimLyrDoc.LayerDefinitions[0]).Renderer as CIMUniqueValueRenderer;

                    //Apply the renderer to the feature layer
                    featureLayer?.SetRenderer(rendererFromLayerFile);
                }

            });
        }

        async private void btnSymbolize_Click(object sender, RoutedEventArgs e)
        {
            try
            {
              
                Map map = MapView.Active.Map;

                this.Cursor = Cursors.Wait;

                await ModifyLayerSymbologyFromLyrFileAsync(map.GetLayersAsFlattenedList().OfType<FeatureLayer>());
            }

            catch  (Exception ex)
            {
                ArcGIS.Desktop.Framework.Dialogs.MessageBox.Show(ex.ToString());
            }
            
            finally
            {
                this.Cursor = Cursors.Arrow;
                MessageBox.Show("DONE");
                this.Close();
            }            
        }
    }
}

‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍
GKmieliauskas
Esri Regular Contributor

Hi Uma,

What about the same functionality for raster layers? I have installed 2.4 and found that RasterLayer does not have SetRenderer method.

My workarounds some how lock my data and I can't run calculations on the same database on ArcMap.

The place that locks my database is creating raster layer with symbology.

0 Kudos
UmaHarano
Esri Regular Contributor

Hi Gintautas

Here is a code snippet for using this pattern with Raster layers. Raster use the Colorizer - Get/SetColorizer.

private static async Task ModifyLayerSymbologyFromLyrFileAsync(IEnumerable<RasterLayer> featureLayers, string layerFile)
        {
            await QueuedTask.Run(() =>
            {
                foreach (var featureLayer in featureLayers)
                {
                    //Get the Layer Document from the lyrx file
                    var lyrDocFromLyrxFile = new LayerDocument(layerFile);
                    var cimLyrDoc = lyrDocFromLyrxFile.GetCIMLayerDocument();

                    //Get the renderer from the layer file
                    //This lyr file has a unique value renderer.
                    var rendererFromLayerFile = ((CIMRasterLayer)cimLyrDoc.LayerDefinitions[0]).Colorizer as CIMRasterUniqueValueColorizer;

                    //Apply the renderer to the feature layer
                    featureLayer?.SetColorizer(rendererFromLayerFile);
                }

            });
        }

Thanks

Uma

0 Kudos
GKmieliauskas
Esri Regular Contributor

Hi Uma,

Thanks for reply.

Your snippet doesn’t work with old lyr files (crashes with HResult=0x80004005 Message=Could not determine encoding).

It does not work with “Classify“ colorizer in lyrx file too.

The only way what works is loading lyr(x) file and updating data source. But there are other problems with locking as I mentioned earlier.

0 Kudos
BrianBulla
Honored Contributor

Hi Uma Harano‌,

Sorry to come back to this, but I'm running across a weird issue where a newly created layer file will not work with the code you provided from last summer.  This is the first time I've come across this, and not really sure what might be going on.

The code does still update for the .lyrx that have always been there, but will not work for this new one I just created (Watermain_Breaks.lyrx).  I've attached a sample of it.  Is there anything in it that might be causing issues??

Thanks,

0 Kudos