Using Color Ranges to Visualize Data Distribution in xamTreemap

Nikolay Arhangelov / Thursday, June 2, 2011

The xamTreemap™ control has a predefined value mapper called ColorMapper, which distributes a color among the mapped nodes. The purpose of this mapper is to help users easily spot the distribution of certain values in their data – so the main thing in configuring the mapper is setting the colors for the distribution, not setting colors and their corresponding values.


 
The concept of color distribution of the ColorMapper is providing lower value nodes with colors closer to the From color, and the higher value nodes – with colors closer to the To color.
This post will guide you on how to create a custom mapper that uses a range of colors to rank the nodes.

Defining a Custom Mapper

The xamTreemap control allows users to define custom value mappers. Here is the outline of a custom mapper:

    public class MultiColorMapper : RangeMapper
    {
        public override void MapValue(TreemapNode node)
        { }

        public override void ResetValue(TreemapNode node)
        { }        
    }

 

We are going to use the base RangeMapper class to create the MultiColorMapper.

Note: The ResetValue method is not going to be used.

Multi-Color Mapper

The mapper will apply a color to a node when the MapValue method is called for it. Before the mapping, the base Value Mapper class internally finds the lowest and larges values pointed by ValuePath – these values are then stored in the ActualDataMinimum and ActualDataMaximum properties. For each node, the Value Mapper base class calls MapValue, which should calculate the relative offset of the current node compared to the ActualDataMinimum and ActualDataMaximum.

In the case of the MultiColorMapper, after the offset of the node relative to the minimum and maximum values is calculated, this offset will be used to produce the offset relative to the nearest gradient stops.


 
If A and B are the nearest gradient stops, the value of O(AB) will be used to get a color between the colors of A and B.

A MultiColorMapper with two gradient stops (offsets 0 and 1) will work as a ColorMapper.

Gradient Stops

To define the colors and their offsets, use a GradientStop object (the objects will be stored in a GradientStopCollection object).

Note: The GradientStop and GradientStopCollection classes are located in the System.Windows.Media namespace. 

The GradientStops property:

        private GradientStopCollection _gradientStops = new GradientStopCollection();

        /// <summary>
        /// Gets or sets the collection with the gradient stops.
        /// </summary>
        public GradientStopCollection GradientStops
        {
            get
            {
                return _gradientStops;
            }
            set
            {
                _gradientStops = value;
                _gradientStops.OrderBy(x => x.Offset);
                RaisePropertyChanged("GradientStops");
            }
        }

 

Note: The gradient stops are ordered by Offset when the collection is set so that it would be easier to find the nearest gradient stops.

The Map Value Method

        /// <summary>
        /// This method is called for all nodes of the <see cref="XamTreemap"/> control and applies a mapping to them.
        /// </summary>
        /// <param name="node">The current node.</param>
        public override void MapValue(TreemapNode node)
        {
            //Check if the node is of the appropriate value type,
            //has a data context, and the collection of gradient stops
            //has more than one items 
            if (!string.IsNullOrEmpty(this.ValueTypeName)
                && node.DataContext != null
                && node.DataContext.GetType().Name != this.ValueTypeName
                && GradientStops.Count > 1) return;

            Type type = node.DataContext.GetType();

            //Check if the node should be mapped
            if (type.Name == this.ValueTypeName)
            {
                //Get the value pointed by ValuePath
                double value = 
                    Convert.ToDouble(GetMappedValue(node.DataContext, type));

                //Get the node's offset relative to the Min and Max
                double relativeValue = GetRelativeValue(value);

                //Get the gradient stops with the lowest and highest
                //Offset values
                GradientStop min = _gradientStops.First();
                GradientStop max = _gradientStops.Last();

                SolidColorBrush brush = new SolidColorBrush();

                //Check if the node's offset is outside of the range defined
                //by the min and max gradient stops
                if (relativeValue <= min.Offset)
                {
                    brush.Color = min.Color;
                }
                else if (relativeValue >= max.Offset)
                {
                    brush.Color = max.Color;
                }

                //Going here means that the node's offset is between the
                //min and max gradient stops
                else
                {
                    //Get the closest gradeint stop on the left of the node
                    GradientStop from = _gradientStops
                        .TakeWhile(x => x.Offset < relativeValue)
                        .Last();

                    //Get the closest gradient stop on the right of the node
                    GradientStop to = _gradientStops
                        .First(x => x.Offset > relativeValue);

                    //Get the offset of the node relative ot the closest to it
                    //gradient stops
                    double relativeToGradientStopsValue = 
                        GetRelativeValue(from.Offset, relativeValue, to.Offset);

                    //Get a color based on the closest gradient stops 
                    //and the offset of the node
                    brush.Color = 
                        GetColor(relativeToGradientStopsValue, from.Color, to.Color);
                }

                //Set the Fill porperty of the node
                node.Fill = brush;
            }
        }

 

Usage

				<ValueMappers:MultiColorMapper x:Name="Mapper" ValueTypeName="Product" ValuePath="StandardCost">
					<ValueMappers:MultiColorMapper.GradientStops>
						<GradientStop Offset=".0" Color="#FF3AB5E9" />
						<GradientStop Offset=".25" Color="#FFfdbd48" />
						<GradientStop Offset=".5" Color="#FFff6a6f" />
						<GradientStop Offset=".75" Color="#FF9e73c1" />
						<GradientStop Offset="1" Color="#FF793bac" />
					</ValueMappers:MultiColorMapper.GradientStops>
				</ValueMappers:MultiColorMapper>

 

Sample Project

You can download a Silverlight project with the full implementation of the MultiColorMapper along with a sample data source.

Related Help Topics