Say I have a geodatabase containing a feature class with the following fields:
objectid mapunit geom
The mapunits are defined in another table that includes a color:
objectid mapunit(unique) color
How can I render the features in the feature class in the color corresponding to their mapunit?
I've experimented with unique value renderers, but I can't figure out how to use them to assign a specific color to a specific value of mapunit.
It seems like this was possible in an older .NET SDK ("Unique value renderer section" in https://developers.arcgis.com/net/10-2/desktop/guide/symbols-and-renderers.htm). But with ArcGIS Pro, you have to specify a color ramp and one or more fields to examine. I don't see a way to map to specific values of those fields.
If I specify a color ramp from the colors in the definition table, it seems like the renderer uses a first-come assignment to the values it finds in the mapunit field of the feature class--I don't have control over that mapping.
Does anyone have any suggestions?
Solved! Go to Solution.
I attached the sample code to create a Unique Value Renderer. I also attached a sample Pro project that works in conjunction with the sample code. The key code snippet is the creation of the unique value renderer:
/// <summary>
/// You must call this method on the MCT!
/// </summary>
/// <returns></returns>
private CIMRenderer CreateUniqueValueRendererUsingColorTable(FeatureLayer lyrToColor,
StandaloneTable colorTable)
{
//All of these methods have to be called on the MCT
if (OnUIThread)
throw new ArcGIS.Core.CalledOnWrongThreadException();
//make a dictionary with MapUnit and Color
var dicMapUnitColor = new Dictionary<int, string>();
using (var rowCursor = colorTable.Search())
{
while (rowCursor.MoveNext())
{
using (var anyRow = rowCursor.Current)
{
dicMapUnitColor.Add((int)anyRow["MapUnit"], anyRow["Color"].ToString());
}
}
}
//Create the Unique Value Renderer
CIMUniqueValueRenderer uniqueValueRenderer = new CIMUniqueValueRenderer()
{
// set the value field
Fields = new string[] { "MapUnit" }
};
//Construct the list of UniqueValueClasses
List<CIMUniqueValueClass> classes = new List<CIMUniqueValueClass>();
//define the unique values for each dicMapUnitColor entry
foreach (var key in dicMapUnitColor.Keys)
{
var lstValue = new List<CIMUniqueValue>()
{
new CIMUniqueValue()
{
FieldValues = new string[] { key.ToString() }
}
};
var namedColor = System.Drawing.Color.FromName(dicMapUnitColor[key]);
var theColor = CIMColor.CreateRGBColor(namedColor.R, namedColor.G, namedColor.B);
classes.Add(
new CIMUniqueValueClass()
{
Values = lstValue.ToArray(),
Label = $@"Color: {dicMapUnitColor[key]}",
Visible = true,
Editable = true,
Symbol = new CIMSymbolReference() {
Symbol = SymbolFactory.Instance.ConstructPointSymbol(
SymbolFactory.Instance.ConstructMarker(theColor, 20, SimpleMarkerStyle.Pushpin)) }
}
);
}
//Add the classes to a group (by default there is only one group or "symbol level")
// Unique value groups
CIMUniqueValueGroup groupOne = new CIMUniqueValueGroup()
{
Heading = "By Color",
Classes = classes.ToArray()
};
uniqueValueRenderer.Groups = new CIMUniqueValueGroup[] { groupOne };
//Draw the rest with the default symbol
uniqueValueRenderer.UseDefaultSymbol = true;
uniqueValueRenderer.DefaultLabel = "All other values";
var defaultColor = CIMColor.CreateRGBColor(215, 215, 215);
uniqueValueRenderer.DefaultSymbol = new CIMSymbolReference()
{
Symbol = SymbolFactory.Instance.ConstructPointSymbol(
SymbolFactory.Instance.ConstructMarker(defaultColor, 15, SimpleMarkerStyle.Diamond))
};
return uniqueValueRenderer as CIMRenderer;
}
Running the add-in using this input:
and then changes the colors using the lookup:
The link you are referring to covers the 'Runtime SDK' not the 'Pro SDK'. I will try to publish a small sample later today.
I attached the sample code to create a Unique Value Renderer. I also attached a sample Pro project that works in conjunction with the sample code. The key code snippet is the creation of the unique value renderer:
/// <summary>
/// You must call this method on the MCT!
/// </summary>
/// <returns></returns>
private CIMRenderer CreateUniqueValueRendererUsingColorTable(FeatureLayer lyrToColor,
StandaloneTable colorTable)
{
//All of these methods have to be called on the MCT
if (OnUIThread)
throw new ArcGIS.Core.CalledOnWrongThreadException();
//make a dictionary with MapUnit and Color
var dicMapUnitColor = new Dictionary<int, string>();
using (var rowCursor = colorTable.Search())
{
while (rowCursor.MoveNext())
{
using (var anyRow = rowCursor.Current)
{
dicMapUnitColor.Add((int)anyRow["MapUnit"], anyRow["Color"].ToString());
}
}
}
//Create the Unique Value Renderer
CIMUniqueValueRenderer uniqueValueRenderer = new CIMUniqueValueRenderer()
{
// set the value field
Fields = new string[] { "MapUnit" }
};
//Construct the list of UniqueValueClasses
List<CIMUniqueValueClass> classes = new List<CIMUniqueValueClass>();
//define the unique values for each dicMapUnitColor entry
foreach (var key in dicMapUnitColor.Keys)
{
var lstValue = new List<CIMUniqueValue>()
{
new CIMUniqueValue()
{
FieldValues = new string[] { key.ToString() }
}
};
var namedColor = System.Drawing.Color.FromName(dicMapUnitColor[key]);
var theColor = CIMColor.CreateRGBColor(namedColor.R, namedColor.G, namedColor.B);
classes.Add(
new CIMUniqueValueClass()
{
Values = lstValue.ToArray(),
Label = $@"Color: {dicMapUnitColor[key]}",
Visible = true,
Editable = true,
Symbol = new CIMSymbolReference() {
Symbol = SymbolFactory.Instance.ConstructPointSymbol(
SymbolFactory.Instance.ConstructMarker(theColor, 20, SimpleMarkerStyle.Pushpin)) }
}
);
}
//Add the classes to a group (by default there is only one group or "symbol level")
// Unique value groups
CIMUniqueValueGroup groupOne = new CIMUniqueValueGroup()
{
Heading = "By Color",
Classes = classes.ToArray()
};
uniqueValueRenderer.Groups = new CIMUniqueValueGroup[] { groupOne };
//Draw the rest with the default symbol
uniqueValueRenderer.UseDefaultSymbol = true;
uniqueValueRenderer.DefaultLabel = "All other values";
var defaultColor = CIMColor.CreateRGBColor(215, 215, 215);
uniqueValueRenderer.DefaultSymbol = new CIMSymbolReference()
{
Symbol = SymbolFactory.Instance.ConstructPointSymbol(
SymbolFactory.Instance.ConstructMarker(defaultColor, 15, SimpleMarkerStyle.Diamond))
};
return uniqueValueRenderer as CIMRenderer;
}
Running the add-in using this input:
and then changes the colors using the lookup:
This is very similar to what I came up with after stumbling across https://community.esri.com/thread/217968-unique-value-renderer-specifying-values.
The main difference is that I directly populate the list of CIMUniqueValueClass while traversing the RowCursor rather than creating an intermediate dictionary.
Thanks so much for your help Wolfgang. None of this can be intuited from any of the code examples I found outside of this community board.
using (Table table = geodatabase.OpenDataset<Table>("DescriptionOfMapUnits")) {
QueryFilter queryFilter = new QueryFilter {
SubFields = "MapUnit, AreaFillRGB",
PostfixClause = "ORDER BY objectid"
};
using (RowCursor rowCursor = table.Search(queryFilter, false)) {
List<CIMUniqueValueClass> listUniqueValueClasses = new List<CIMUniqueValueClass>();
while (rowCursor.MoveNext()) {
using (Row row = rowCursor.Current) {
string mu = Convert.ToString(row["MapUnit"]);
string colorString = Convert.ToString(row["AreaFillRGB"]);
string[] strVals = colorString.Split(';');
// Create a "CIMUniqueValueClass" for the map unit.
List<CIMUniqueValue> listUniqueValues = new List<CIMUniqueValue> {
new CIMUniqueValue {
FieldValues = new string[] { mu }
}
};
var cF = ColorFactory.Instance;
CIMUniqueValueClass uniqueValueClass = new CIMUniqueValueClass {
Editable = true,
Label = mu,
//Patch = PatchShape.Default,
Patch = PatchShape.AreaPolygon,
Symbol = SymbolFactory.Instance.ConstructPolygonSymbol(cF.CreateRGBColor(Double.Parse(strVals[0]), Double.Parse(strVals[1]), Double.Parse(strVals[2]))).MakeSymbolReference(),
Visible = true,
Values = listUniqueValues.ToArray()
};
listUniqueValueClasses.Add(uniqueValueClass);
}
}
//Create a list of CIMUniqueValueGroup
CIMUniqueValueGroup uvg = new CIMUniqueValueGroup {
Classes = listUniqueValueClasses.ToArray(),
};
List<CIMUniqueValueGroup> listUniqueValueGroups = new List<CIMUniqueValueGroup> { uvg };
//Create the CIMUniqueValueRenderer
DataHelper.mapUnitRenderer = new CIMUniqueValueRenderer {
UseDefaultSymbol = false,
Groups = listUniqueValueGroups.ToArray(),
Fields = new string[] { "mapunit" }
};
}
}