Select to view content in your preferred language

Problems using CIMRasterClassBreak

367
2
Jump to solution
06-24-2024 11:57 AM
SteveCole
Honored Contributor

Having some issues using CIMRasterClassBreak that are really frustrating and I can't seem to solve it. For background, I'm trying to migrate an add-in functionality from Arcmap where a user selects a raster from those loaded into the TOC, and based on a starting elevation, number of classes, and interval between the classes, it reclasses the raster for display. This is helpful for lidar where you're trying to see subtle variations in topography.

 

Anyways, the ChangeColorizerForRasterLayer community sample largely pointed me in the right direction in terms of what part of the SDK I needed to focus on and I'm pretty much there, except for the actual task of modifying the class breaks. Here is the core code of my process:

        public static async Task SetToClassifyColorizer(BasicRasterLayer basicRasterLayer, Int32 numClasses, Double startElev, Double theIntervalSize)
        {
            // Defines values for parameters in colorizer definition.
            string fieldName = "Value";
            ClassificationMethod classificationMethod = ClassificationMethod.DefinedInterval;
            int numberofClasses = numClasses;
            string colorRampStyle = "ArcGIS Colors";
            string colorRampName = "Temperature";

            await QueuedTask.Run(async () =>
            {
                // Gets a color ramp from a style.
                IList<ColorRampStyleItem> rampList = GetColorRampsFromStyleAsync(Project.Current, colorRampStyle, colorRampName);
                CIMColorRamp colorRamp = rampList[0].ColorRamp;

                // Creates a new Classify Colorizer Definition using defined parameters.
                ClassifyColorizerDefinition classifyColorizerDef = new ClassifyColorizerDefinition(fieldName, numberofClasses, classificationMethod, colorRamp);
                classifyColorizerDef.IntervalSize = theIntervalSize;

                // Creates a new Classify colorizer using the colorizer definition created above.
                CIMRasterClassifyColorizer newColorizer = await basicRasterLayer.CreateColorizerAsync(classifyColorizerDef) as CIMRasterClassifyColorizer;
                var templateClassBreak = newColorizer.ClassBreaks[0];
                var theClassBreaks = new ArcGIS.Core.CIM.CIMRasterClassBreak[numberofClasses];

                var curValue = startElev;

                //Manually create a replacement set of class breaks. These needs a color symbol but that will be done in a separate step so just
                //asign it null for the time being
                for (int i = 0; i < numberofClasses; i++)
                {
                    var curClassBreak = newColorizer.ClassBreaks[0];
                    
                    if (i == 0)
                    {
                        curClassBreak.Label = "<= " + curValue.ToString();
                    } else if (i == (numberofClasses -1))
                    {
                        curClassBreak.Label = ">= " + curValue.ToString();
                    } else
                    {
                        var preVal = curValue - theIntervalSize;
                        curClassBreak.Label =  preVal.ToString() + " - " + curValue.ToString();
                    }
                    curClassBreak.UpperBound = curValue;
                    curClassBreak.Color = null;

                    theClassBreaks[i] = curClassBreak;
                    curValue = curValue + theIntervalSize;
                    curClassBreak = null;
                }
                //Now make the color ramp for the classes
                var colors = ColorFactory.Instance.GenerateColorsFromColorRamp(newColorizer.ColorRamp, numberofClasses);
                
                var c = 0;
                foreach (var cbreak in theClassBreaks)
                {
                    //assign the generated colors to each class break in turn
                    cbreak.Color = colors[c++];
                }

                newColorizer.ClassBreaks = theClassBreaks;

                // Sets the newly created colorizer on the layer.
                basicRasterLayer.SetColorizer(newColorizer);
            });
        }

 

The loop of manually creating class breaks is where things go sideways. The code above creates a new class break with the expected number of classes but, as the loop iterates, it adds whatever the "current" value is but it also updates all previous values in the class break collection with the current value's settings. So, at the end of the loop, all values in the class break object are the same.

I'm at a loss to figure out what I'm doing wrong here. Help?

Steve

 

0 Kudos
1 Solution

Accepted Solutions
SteveCole
Honored Contributor

Hi, I just came to post an update but just saw your post. I solved the issue but a different way. Again, the documentation leaves a lot to be desired which adds to confusion about all of this. The solution I stumbled across may fall in line with what you were suggesting but the curClassBreak variable was the source of the issue. Essentially, I changed how that variable is initialized within the loop. That code section is now:

                for (int i = 0; i < numberofClasses; i++)
                {
                    var curClassBreak = new CIMRasterClassBreak();
                    
                    if (i == 0)
                    {
                        curClassBreak.Label = "<= " + curValue.ToString();
                    } else if (i == (numberofClasses -1))
                    {
                        curClassBreak.Label = ">= " + curValue.ToString();
                    } else
                    {
                        var preVal = curValue - theIntervalSize;
                        curClassBreak.Label =  preVal.ToString() + " - " + curValue.ToString();
                    }
                    curClassBreak.UpperBound = curValue;
                    curClassBreak.Color = null;

                    theClassBreaks[i] = curClassBreak;
                    curValue = curValue + theIntervalSize;
                    curClassBreak = null;
                }

I kept thinking of CIMRasterClassBreak as a collection of class break values rather than as a single, specific class break instance that's part of a greater collection. My understanding wasn't helped by the fact that when I examined the class break variable during debugging I saw a number of other properties & methods that aren't documented in the SDK (I'm guessing they're inherited since it's an array).

Anyways, it now technically works as intended. The length of time it takes to accomplish this is horrible compared to the same operation using my Arcmap Add-in but I think this is tied to my lack of understanding of await/async which seems to be much greater than VB.NET or VBA from the good old days.

View solution in original post

0 Kudos
2 Replies
GKmieliauskas
Esri Regular Contributor

Hi,

Change index of array:

// From
var curClassBreak = newColorizer.ClassBreaks[0];

// To 
var curClassBreak = newColorizer.ClassBreaks[i];
0 Kudos
SteveCole
Honored Contributor

Hi, I just came to post an update but just saw your post. I solved the issue but a different way. Again, the documentation leaves a lot to be desired which adds to confusion about all of this. The solution I stumbled across may fall in line with what you were suggesting but the curClassBreak variable was the source of the issue. Essentially, I changed how that variable is initialized within the loop. That code section is now:

                for (int i = 0; i < numberofClasses; i++)
                {
                    var curClassBreak = new CIMRasterClassBreak();
                    
                    if (i == 0)
                    {
                        curClassBreak.Label = "<= " + curValue.ToString();
                    } else if (i == (numberofClasses -1))
                    {
                        curClassBreak.Label = ">= " + curValue.ToString();
                    } else
                    {
                        var preVal = curValue - theIntervalSize;
                        curClassBreak.Label =  preVal.ToString() + " - " + curValue.ToString();
                    }
                    curClassBreak.UpperBound = curValue;
                    curClassBreak.Color = null;

                    theClassBreaks[i] = curClassBreak;
                    curValue = curValue + theIntervalSize;
                    curClassBreak = null;
                }

I kept thinking of CIMRasterClassBreak as a collection of class break values rather than as a single, specific class break instance that's part of a greater collection. My understanding wasn't helped by the fact that when I examined the class break variable during debugging I saw a number of other properties & methods that aren't documented in the SDK (I'm guessing they're inherited since it's an array).

Anyways, it now technically works as intended. The length of time it takes to accomplish this is horrible compared to the same operation using my Arcmap Add-in but I think this is tied to my lack of understanding of await/async which seems to be much greater than VB.NET or VBA from the good old days.

0 Kudos