~/overview/1.5.mappers.md

Mappers

You can plot anything in a chart as soon as you let the library how to handle that object, LiveCharts already supports the types short, int, long, float, double, decimal, their nullable versions short?, int?, long?, float?, double?, decimal?, and finally some common objects defined by the library WeightedPoint (to make bubble charts), ObservableValue, ObservableValueF (float), ObservablePoint (use full to specify both, X and Y), and ObservablePointF.

A mapper is a function that maps an element in the Series.Values collection to a point in a chart, this function will run for every item in the Series.Values collection, and will let the library where and how to place that object in a chart. The mapper function takes the an item from or values collection and the point in the chart as arguments, you must populate the point PrimaryValue (normally the Y coordinate), SecondaryValue (normally the X coordinate) and TertiaryValue (normally the weight in a weighted series) properties.

Lets take the following City class as an example:

public class City 
{
    public string Name { get; set; }
    public double Population { get; set; }
    public double Density { get; set; }
}

Having an array of the City class:

var cities = new City[]
{
    new City { Name = "Tokio", Population = 10, Density = 5 },
    new City { Name = "Cape Town", Population = 9, Density = 6 },
    new City { Name = "New York", Population = 8, Density = 7 }
}

Finally to see our cities array in a chart we would:

myChartControlSeries = new ObservableCollection<ISeries> 
{
    new LineSeries<City> { Values = cities }
}

If we run our application now, the previous code will throw an exception because LiveCharts currently does not know how to display the City class in a chart, thus we need to register a mapper for our City class.

Global mappers

The following code registers the City class globally, this means that every time the City class is used in a chart all over our application, the library will use the mapper we indicated.

// this code must be placed where your application or view starts
LiveCharts.Configure(config =>
    config
        .HasMap<City>((city, point) =>
        {
            // use the city Population property as the primary value
            point.PrimaryValue = (float)city.Population;
            // and the index of the city in our cities array as the secondary value
            point.SecondaryValue = point.Context.Index;
        }));

Run the application again and the exception is gone, LiveCharts now knows that when it founds a City instance it must use the Population property and the index of the instance in our collection, but if we required to plot the Density property, then we would build a mapper like the following:

// this code must be placed where your application or view starts
LiveCharts.Configure(config =>
    config
        .HasMap<City>((city, point) =>
        {
            point.PrimaryValue = (float)city.Density;
            point.SecondaryValue = point.Context.Index;
        }));

Global mappers are unique for a type, this means that now our previous mapper will be ignored, and from now on LiveCharts will use the Density property every time it founds a City instance.

Global mappers should ideally register only once when your application starts.

Local mappers

Imagine the case were we require a chart that displays the Density and a chart that display the Population property, in this case global mappers are not enough because global mappers are unique for a type, thus we will use the Series.Mapping property to override the global configuration for a specific series:

myChartControlSeries = new ObservableCollection<ISeries> 
{
    new LineSeries<City>
    { 
        Values = cities,
        Mapping = (city, point) =>
        {
            // use the Population property in this series
            point.PrimaryValue = (float)city.Population;
            point.SecondaryValue = point.Context.Index;
        }
    }, 
    new LineSeries<City>
    {
        Values = cities,
        Mapping = (city, point) =>
        {
            // but for this series use the Density
            point.PrimaryValue = (float)city.Density;
            point.SecondaryValue = point.Context.Index;
        }
    }
}

Now even we are on the same chart and we are using the same cities instance, every series is drawing a different property in the user interface.

The Series.Mapping property is null by default which means that it will try to pull a mapper from the global settings, if Series.Mapping is null and the type is not registered globally, the library will throw an exception.

Point Context

Notice we used the point.Context.Index property to grab the index of the city in our array, LiveCharts exposes many properties in a ChartPointContext instance, in the case of a mapper you normally only need the Index but this property lets you know more about a specific point, such as the series or the chart that contains the point.

Orientation

It is also important to mention that LiveCharts supports vertical and horizontal series, the ChartPoint.PrimaryValue is not always the Y coordinate (in a Cartesian coordinate system), commonly you could see the PrimaryValue as the Y coordinate, but for example in the case of a RowSeries where its orientated horizontally, the PrimaryValue property is the X coordinate, As a general rule, for vertical orientated series PrimaryValue is the Y coordinate and SecondaryValue is the X coordinate, for horizontal orientated series PrimaryValue is the X coordinate and SecondaryValue is the Y coordinate, TertiaryValue is normally only used in a weighted chart, such as a bubble series, where every point has a different diameter.

You really do not need to do anything special to handle orientation, this is just a reminder to let you know how the library works, the following example will work for both, ColumnSeries and RowSeries.

myChartControlSeries = new ObservableCollection<ISeries> 
{
    new ColumnSeries<City> { Values = cities }, 
    new RowSeries<City> { Values = cities }
}

// the following code must be placed where your application or view starts
LiveCharts.Configure(config =>
    config
        .HasMap<City>((city, point) =>
        {
            point.PrimaryValue = (float)city.Population;
            point.SecondaryValue = point.Context.Index;
        }));