How to render color of individual features based on field in another table

454
3
Jump to solution
10-20-2020 10:38 AM
Douglas_DMeredith
New Contributor II

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?

Tags (1)
0 Kudos
1 Solution

Accepted Solutions
Wolf
by Esri Regular Contributor
Esri Regular Contributor

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:

View solution in original post

3 Replies
Wolf
by Esri Regular Contributor
Esri Regular Contributor

The link you are referring to covers the 'Runtime SDK' not the 'Pro SDK'.  I will try to publish a small sample later today.

0 Kudos
Wolf
by Esri Regular Contributor
Esri Regular Contributor

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:

Douglas_DMeredith
New Contributor II

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" }
		};

	}
}
‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍
0 Kudos