TTilton-esristaff

Heat map renderer: Map it like it's hot!

Blog Post created by TTilton-esristaff Employee on Mar 28, 2018

If you've ever had to show the density of point features on a map, you may already be familiar with the concept of a heat map, which allows you to generalize the display of points according to their density. A heat map provides an excellent tool for discovering patterns in your data. In addition to looking at density of point location, you can also provide a field in your dataset with which to weight the density. This allows you to look at the density of things like sales (dollars, for example) instead of just the density of customers over an area.

 

Heat maps are displayed with a color ramp that usually shows dense areas with a darker color (red, for example) and sparse areas with a light color (such as yellow). You may have used a heat map renderer to display your point data in the ArcGIS Online map viewer or ArcGIS Pro. The heat map below shows the density of earthquakes weighted with a field that contains the magnitude.

Unique value points to heat map renderer

"Sounds great". You might be saying. "I'm excited to try it in my ArcGIS Runtime for .NET app"!
"Um, yeah. About that." I would say, sheepishly looking at my shoes.

 

While there is support in ArcGIS Runtime for reading things like a heat map renderer from a web map, there's no API exposed (as of v100.2.1) that allows you to construct one and apply it to a point layer without crafting the raw JSON definition. Fortunately, with a little effort, there's a way to make it much easier to work with.

 

In a previous blog, I described how to create a serializable class to define labeling properties to apply to a layer. I'll describe the same technique here to define a heat map renderer class that can be serialized to JSON and applied to your point layer. If you don't really care about the details and just want to start using it, you can download the completed class and sample project and give it a try.

 

  1. Start Visual Studio and create a new ArcGIS Runtime SDK for .NET app. 
  2. Use the NuGet Package Manager to add the Newtonsoft.Json package to the project.
  3. Add a new class to the project. Let's name it HeatMapRenderer.cs. Add the following using statements at the top of the class.
    using Newtonsoft.Json;
    using System;
    using System.Collections.Generic;
    using System.Windows.Media;
  4. Create properties for everything expected in the JSON definition of the heat map renderer class. This information is defined under heatmapRenderer in the Web map specification. The specification defines a "blurRadius" value, for example. This could be implemented with the following simple long property. Note that the name of the property in the class does not need to match the name in the specification. 
    public long BlurRadius { get; set; }
    Based on the specification, here are the properties (and corresponding data type) needed for the heat map renderer class:
    • blurRadius (long)
    • colorStops (list of objects)
    • field (string)
    • maxPixelIntensity (double)
    • minPixelIntensity (double)
    • type (string).
  5. OK, the list above looks pretty straightforward, except for the property that returns a list of objects. Go ahead and create properties for everything except colorStops (we'll circle back to that one). 
  6. The Type property needs to be set with "heatmap" as the default, as shown here.
    public string Type { get; set; } = "heatmap";
  7. After you've defined the properties, in order to make them serializable you'll need to decorate them with the  JsonProperty attribute. The string you supply will be used in the output JSON and must match the name used in the Web map specification. Add the appropriate JsonProperty attribute to all properties, as shown below for the BlurRadius property.
    [JsonProperty("blurRadius")]
    public long BlurRadius { get; set; }
  8. The colorStops value is defined with a list of objects. What kind of object do we need here? In the specification, the JSON describes a colorStop class with two properties: color and ratio. You'll therefore need to create a new class with those properties. Let's call the class ColorStop.
    public partial class ColorStop
    {
        [JsonProperty("ratio")]
        public double Ratio { get; set; }

        [JsonProperty("color")]
        public int[] Color { get; set; }
    }
  9. The ratio property is a double. Color is a four value array (R, G, B, A). The constructor for ColorStop will accept a ratio (double) and a Color. The color values are used to set the array.
    public ColorStop(double ratio, Color color)
    {
        Ratio = ratio;
        Color = new int[] { color.R, color.G, color.B, color.A };
    }
  10. Return to the HeatMapRenderer class and add a property to hold the color stops. Initialize the property with an empty list of color stops.
    [JsonProperty("colorStops")]
    public List<ColorStop> ColorStops { get; set; } = new List<ColorStop>();
  11. Add some helper methods to add or clear color stops in the collection.
    public void AddColorStop(double ratio, Color color)
    {
        if (ratio > 1.0 || ratio < 0.0) { throw new Exception("Argument 'ratio' must be a value between 0 and 1."); };

        ColorStop stop = new ColorStop(ratio, color);
        ColorStops.Add(stop);
    }

    public void ClearColorStops()
    {
        ColorStops.Clear();
    }
  12. Finally, add a ToJson method that returns the JSON representation of the class.
    public string ToJson()
    {
        return JsonConvert.SerializeObject(this);
    }
  13. You should now be able to use the new HeatMapRenderer class to write code like the following to apply a heat map renderer to a point feature layer!
    // Create a new HeatMapRenderer with info provided by the user.
    HeatMapRenderer heatMapRendererInfo = new HeatMapRenderer
    {
        BlurRadius = blurRadius,
        MinPixelIntensity = minIntensity,
        MaxPixelIntensity = maxIntensity
    };

    // Use a selected field to weight the point density if the user chooses to do so.
    if (UseFieldCheckBox.IsChecked == true)
    {
        heatMapRendererInfo.Field = (FieldComboBox.SelectedValue as Field).Name;
    } 

    // Add the chosen color stops (plus transparent for empty areas).
    heatMapRendererInfo.AddColorStop(0.0, Colors.Transparent);
    heatMapRendererInfo.AddColorStop(0.10, (Color)StartColorComboBox.SelectedValue);
    heatMapRendererInfo.AddColorStop(1.0, (Color)EndColorComboBox.SelectedValue);

    // Get the JSON representation of the renderer class.
    string heatMapJson = heatMapRendererInfo.ToJson();

    // Use the static Renderer.FromJson method to create a new renderer from the JSON string.
    var heatMapRenderer = Renderer.FromJson(heatMapJson);

    // Apply the renderer to a point layer in the map.
    _quakesLayer.Renderer = heatMapRenderer;

I'd agree with you if you think this is a lot of code to write to implement a heat map renderer. But remember, this class can be used in all your ArcGIS Runtime for .NET apps when you want to apply heat map rendering.

 

Apologies if you were unable to successfully follow the steps above. Fortunately, the attached project contains the HeatMapRenderer class, as well as a WPF app for testing the heat map renderer.

Attachments

Outcomes