How to create an UniqueValueRenderer?

2366
3
09-29-2016 09:19 AM
HoriaTudosie
Occasional Contributor II

I would prefer the method for ArcGIS Desktop described here:

Symbols and renderers—ArcGIS Runtime SDK for .NET | ArcGIS for Developers 

But I cannot make it work in ArcGis PRO.

Instead I use the following code:


var uniqueValueRendererDef = GetUniqueValueRendererDef();
var uniqueValueRenderer = (CIMUniqueValueRenderer)deltaLayer.CreateRenderer(uniqueValueRendererDef);
var classes = uniqueValueRenderer.Groups[0].Classes;
uniqueValueRendererDef = GetUniqueValueRendererDef(classes);
uniqueValueRenderer = (CIMUniqueValueRenderer)deltaLayer.CreateRenderer(uniqueValueRendererDef);
deltaLayer.SetRenderer(uniqueValueRenderer);‍‍‍‍‍‍
...

private UniqueValueRendererDefinition GetUniqueValueRendererDef(CIMUniqueValueClass[] classes = null)
{
    string[] fields = { Model.DbFieldOperation.Key }; //field to be used to retrieve unique values

    CIMMultiLayerSymbol symbol = null;

    switch (DeltaLayer.ShapeType)
    {
...
        case esriGeometryType.esriGeometryPolygon:
            symbol = SymbolFactory.ConstructPolygonSymbol(
                            ColorFactory.GreyRGB, SimpleFillStyle.Solid, new CIMSolidStroke()
                            {
                                Width = 3.0,
                                Color = //CIMColor.CreateRGBColor(0, 0, 0, 127)
                                ColorFactory.BlackRGB
                            });
            break;
...
    }

    var symbolTemplate = symbol.MakeSymbolReference();


    var colorRamp = new CIMFixedColorRamp();
    if (classes == null)
    {
        colorRamp.Colors = new[] { ColorFactory.RedRGB, ColorFactory.GreenRGB };
    }
    else
    {
        var colors = new List();
        foreach (var cls in classes)
        {
            switch (cls.Label)
            {
...
                case "DELETE":
                    colors.Add(DeltaLayer.ShapeType == esriGeometryType.esriGeometryPolygon
                        ? ColorFactory.CreateRGBColor(255, 0, 0, 128) : ColorFactory.RedRGB);
                    break;
                default:
                    colors.Add(ColorFactory.GreyRGB);
                    break;
            }
        }
        colorRamp.Colors = colors.ToArray();
    }

    var uniqueValueRendererDef = new UniqueValueRendererDefinition(fields, symbolTemplate, colorRamp);

    return uniqueValueRendererDef;
}

Explanation: The first call to GetUniqueValueRendererDef() in line 2 is to obtain the unique values (in line 4.)

The second call in line 5 produces the desired ColorRamp. The efect was described in my question:

How to use ColorFactory to produce transparent colors for a ColorRamp? 

I cannot simply produce an universal color ramp with 3 (or more) colors because the renter allocates for the second and subsequent values random colors from the ramp when the ramp contains more colors than unique values!

This is inconvenient - I would call it a bug!

Now, the real problem in this post:

For Polynomial Layers I need transparent symbols - this I have already described in the above question.

However, in line 21 I have also asked for a stroke (solid black - although my first preference was transparent as well) 3 units tick.

Not only the stroke does not come, but neither can be added with the symbol editor!

Look at the effect when using a SimpleFillStyle.Vertical filling (line 21)

The cyan rectangle is the selected polygon, but there are 2 polygons there and without stroke line it is impossible to make the difference. Obviously, any king of filling won't replace the transparency on the symbol, but missing strokes makes thinks even worse!

The Format Polygon Symbol dialog shoes:

The solid stroke checkbox came unchecked - why?

When manually checking it, I can see what I want!

(Much better!)

0 Kudos
3 Replies
CharlesMacleod
Esri Regular Contributor

Horia, it looks like the outline is missing from your symbol. Take a look at this: https://github.com/esri/arcgis-pro-sdk/wiki/ProSnippets-MapAuthoring#how-to-construct-a-polygon-symb...

I am not sure about your transparency issue. I will take a look at this.

0 Kudos
HoriaTudosie
Occasional Contributor II

No, is not: From line 20 to line 26 I,m explicitly define the stoke with black color.

See, the thing is that this is a template used in line 31, that together with the colorRamp instance form in line 51 the Unique...Def.

Now: if we create make the line red:

symbol = SymbolFactory.ConstructPolygonSymbol(
 ColorFactory.GreyRGB, SimpleFillStyle.Solid,
 new CIMSolidStroke()
 {
 Width = 1.5,
 Color = CIMColor.CreateRGBColor(255, 0, 0, 127)
 });

When running the application, we get:

Where we see that the line was correctly defined, but the corresponding checkbox was not enabled.

(And no: it is red but not semitransparent!)

Note that ConstructPolygonSymbol has another constructor which takes, instead of the first two parameters, a Fill (something) type, in the same way as the stroke. When using that, the fill symbol comes also unchecked!

(On my wish list there are also:

1.   Use directly (some sort of) UniqueValuesItems instead of colorRamp and symbolTemplate, as I've mentioned in the second line of my question.

2.   Be able to customize the color of the stroke as well,

3.   And, as I've already mentioned, make the fill transparent (if possible, the stroke as well.)

#2 and the () in 3# are optional, but without the rest - is not going to work...

)

0 Kudos
CharlesMacleod
Esri Regular Contributor

Hi Horia, so see if this helps:

Basically, when trying to reverse engineer what is in the CIM - especially the symbology - it is useful to look at the XML in the debugger. So to see how the fill is handled in a polygon symbol, for example, we can use the factory to make a symbol and then disect its parts like so:

CIMStroke outline = SymbolFactory.ConstructStroke(CIMColor.CreateRGBColor(0, 0, 0, trans), 2.0, SimpleLineStyle.Solid);
CIMPolygonSymbol fillWithOutline = SymbolFactory.ConstructPolygonSymbol(ColorFactory.RedRGB,     SimpleFillStyle.Cross, outline);

var xml = fillWithOutline.ToXml();

Then we can set our breakpoint right after the line: "var xml = ....." (or right after).

Now we can inspect the symbol:

XML Visualizer

The basic structure is like so:

Hatch Fill

An outline and two hatch fills in this case and we can inspect the hatch fills to see how they are made also.

Okay, so now we will use this knowledge in our code to allow us to customize some of the symbol properties. I am using the same "Renderer From Scratch" sample as previously in the other post - https://github.com/Esri/arcgis-pro-sdk-community-samples/tree/master/Map-Authoring/CIMExamples - modifying "CreateCIMRendererFromScratch.cs":

First a method to make a custom hatch fill:

internal static CIMFill ConstructHatchFillEx(CIMStroke stroke, double angle, double separation, double transparency)
{
      return new CIMHatchFill() {
           Enable = true,
           Rotation = angle,
          Separation = separation,
         LineSymbol = new CIMLineSymbol() { SymbolLayers = new CIMSymbolLayer[1] { stroke } }
     }  ;
}

Second, a method to make a custom polygon symbol:

internal static CIMSymbol ConstructPolygonSymbolEx(IEnumerable<CIMFill> hatchPattern, CIMStroke outline = null) {

         List<CIMSymbolLayer> symbolLayers = new List<CIMSymbolLayer>();
        if (outline != null)
            symbolLayers.Add(outline);
        foreach (var fill in hatchPattern)
             symbolLayers.Add(fill);
        return new CIMPolygonSymbol() { SymbolLayers = symbolLayers.ToArray() };
}

Now, some code that uses it:

var trans = 50.0;//semi transparent

CIMStroke outline = SymbolFactory.ConstructStroke(CIMColor.CreateRGBColor(0, 0, 0, trans), 2.0, SimpleLineStyle.Solid);

//Some different strokes to play with

var dash = SymbolFactory.ConstructStroke(CIMColor.CreateRGBColor(255, 170, 0, trans), 1.0, SimpleLineStyle.Dash);
var solid = SymbolFactory.ConstructStroke(CIMColor.CreateRGBColor(255, 0, 0, trans), 1.0, SimpleLineStyle.Solid);
var dashDot = SymbolFactory.ConstructStroke(CIMColor.CreateRGBColor(85, 255, 0, trans), 1.0, SimpleLineStyle.DashDotDot);

//Some different hatch fills to play with

//Mimic cross hatch
CIMFill[] crossHatch = {
     ConstructHatchFillEx(dash, 0.0, 5.0, trans),
     ConstructHatchFillEx(dash, 90.0, 5.0, trans)
};
//Mimic diagonal cross
CIMFill[] diagCross = {
     ConstructHatchFillEx(solid, 45.0, 5.0, trans),
     ConstructHatchFillEx(solid, -45.0, 5.0, trans)
};

CIMFill[] diagonal = {
     ConstructHatchFillEx(dashDot, 45.0, 5.0, trans)
};

//Now we will use them

var alaska = new CIMUniqueValueClass() {
    Values = alaskaValues.ToArray(),
    Label = "Alaska",
    Visible = true,
    Editable = true,
    Symbol = new CIMSymbolReference() { Symbol = ConstructPolygonSymbolEx(crossHatch, outline) }
};

classes.Add(alaska);

etc, etc.

alaska

Note: I wrote a blog post on GeoNet about the CIMViewer -

https://community.esri.com/groups/arcgis-pro-sdk/blog/2016/08/04/arcgis-pro-cim-viewer

a sample you can download from here:

https://github.com/Esri/arcgis-pro-sdk-cim-viewer

There are instructions on the github on compiling it, etc. It is an Add-in. Once installed you can manually set the properties, etc. you want on the layer or map via the UI and then inspect the CIM with the CIMViewer to see which properties, etc. you need to set in your code to automate what was done with the UI.

CIM Viewer

0 Kudos