Legend and polygon colors for Leaflet choropleth using Chroma.js

A Leaflet tutorial uses the following hard-coded getColor function to return colors.

// get color 
function getColor(n) {
    return n > 30 ? '#b10026'
           : n > 25 ? '#e31a1c' 
           : n > 25 ? '#fc4e2a' 
           : n > 20 ? '#fd8d3c'
           : n > 15  ? '#feb24c'
           : n > 10  ? '#fed976'
           : n > 5  ? '#ffeda0'
           : n > 0  ? '#ffffcc'
           : '#ffffff';
}

However, I wanted to use Chroma.js to generate the legend colors dynamically. So I needed a new getColor function.

Chroma.js has a variety of methods to return colors. The one I choose was using scale and classes. These can then be sent as variables to a getColor function to return colors to use in legend and map.

scale can be single value or an array of two colors (either as hex values or color words). In my case, the first is a light blue and the second is a darker blue. Chroma.js will then return gradients between these two colors. See colorHex variable below.

classes is an array of legend ‘breaks’ for the color gradients. For example they could be the numerical values from the Leaflet tutorial getColor function above (eg 10, 20, 50, etc). See classBreaks variable below.

The new getColor function is shown below:

var classBreaks = [1,50,100,250,500,1000,2000,3000,6000,9000];
var colorHex = ['#deebf7','#08306b'];

function getColor(n,classBreaks,colorHex) {
    var mapScale = chroma.scale(colorHex).classes(classBreaks);
    if (n === 0) {
        var regionColor = '#ffffff';
    } else { 
        var regionColor = mapScale(n).hex();
    }
    return regionColor
}

This getColor function can then be used as described in the Leaflet tutorial to set choropleth polygon fill colors. It also be used similarly to create the legend by looping through the classes to get a color for each legend entry.

However there is important consideration when creating the legend. Using scale and classes, Chroma.js only returns classes – 1 colors. For example the variable classBreaks array with 10 elements will only return 9 colors. To hack this I push a dummy element (‘999’) to the array so Chroma.js would return 10 colors and then ignore the dummy element when creating the legend.

The legend code is below includes hard-coded zero (0) value set to color white (#ffffff). Looping through the classBreaks each time using getColor function to return legend color based on break value.

var legend = L.control({position: 'topright'});

legend.onAdd = function (map) {
    var div = L.DomUtil.create('div', 'legend');
    div.innerHTML += '<i style="background: #ffffff;"></i>0
';
    classBreaks.push(999); // add dummy class to extend to get last class color, chroma only returns class.length - 1 colors
    for (var i = 0; i &lt; classBreaks.length; i++) {
        if (i+2 === classBreaks.length) {
            div.innerHTML += '<i style="background: ' + getColor(classBreaks[i], classBreaks, colorHex) + ';"></i> ' +
            classBreaks[i] + '+';
            break
        } else {
            div.innerHTML += '<i style="background: ' + getColor(classBreaks[i], classBreaks, colorHex) + ';"></i> ' +
            classBreaks[i] + '–' + classBreaks[i+1] + '<br>';
        }
    }
    return div;
};
legend.addTo(map);

The final map legend looks like this:

5 thoughts on “Legend and polygon colors for Leaflet choropleth using Chroma.js”

  1. Hi Curtis,

    thanks for the tutorial. I tried your code on my map but get error saying “mapScale is not a function”. The error is related to the line var mapScale = chroma.scale(colorHex).classes(classBreaks);

    Do you know what I can do to solve the problem? Any help will be much appreciated.

    Best regards,
    Tim

    1. Hi Tim,

      Check that the variables used in getColor function and mapScale variable eg “n,classBreaks,colorHex” are what is expected eg n = integer and classBreaks and colorHex are arrays.

      Here is result from my page:

      console.log(“n:”, n,”classBreaks:”,classBreaks,”colorHex:”, colorHex,”regionColor:”, regionColor);

      n: 156 classBreaks: (10) [1, 50, 100, 250, 500, 1000, 2000, 3000, 6000, 9000] colorHex: (2) [“#deebf7”, “#08306b”] regionColor: #a9bcd4

      FYI, here is the code in action on functioning web page https://sitrucp.github.io/canada_covid_health_regions/canada.js.

      Curtis

      1. Hi Curtis,

        thanks for your reply. I solved the problem:

        as I am an html newbie, I forgot to change the input at the “fillColor: getColor” function to the required inputs from your function.

        Think before you copy code I guess…

        Tim

          1. Looks like it is working fine but my code was missing a line break at the end of if then statement which should fix that!

            The original post had it but seems WordPress interpreted it as an actual line break and not something I want to show as code.

            Never noticed this before. Weird.

            Code now updated!

            classBreaks[i] + '–' + classBreaks[i+1] + '';

            should be

            classBreaks[i] + '–' + classBreaks[i+1] + 'put line break here';

Leave a Comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.