CARTO VL

CARTO VL is a JavaScript library to create custom Location Intelligence applications over vector rendering.

This library is still under support but it will not be further developed. We don’t recommend starting new projects with it as it will eventually become deprecated. Instead, learn more about our current CARTO for deck.gl library here

Add legends

Maps that symbolize data without the necessary information to decode the symbols are not always effective in communicating their message. In this guide, you will explore how to enrich your visualizations with legends to aid interpretation by providing a visual explanation of point, line, or polygon symbols used on a map with a description of what they represent.

Overview

CARTO VL itself doesn’t provide the functionality to draw legends. Instead, it provides the functionality necessary to build them. What this means is that CARTO VL provides the data you need to create a legend, but drawing that data on the screen (in the form of a legend), is the responsibility of the application developer. The benefit of this is that you have more control over customizing legends for the needs of your specific application.

If you completed the data-driven visualization guide, the map below will look familiar. In that guide, you learned how to symbolize feature properties with a ramp. In this guide, you will learn how to reference that ramp to access data for the legend, and then place the returned content on your map.

At the end of this guide, we also provide a series of examples, that are meant to serve as the legend “building blocks” that you can take and begin to customize on top of for a variety of map types.

Getting started

The map below is the same one as above, a category map that symbolizes US rail accidents by reported weather conditions. Unlike the map above, you will notice that this map has a legend box with a title (“Rail Accidents by Weather”) in the right-hand corner, but is missing the legend information to help interpret what weather type each color on the map represents.

Let’s add that information!

To get started, copy and paste the code for this map and save it as accidents.html:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
<!DOCTYPE html>
<html>

<head>
    <title>Rail accident weather | CARTO</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta charset="UTF-8">
    <script src="../../../dist/carto-vl.js"></script>

    <script src="https://api.tiles.mapbox.com/mapbox-gl-js/v0.50.0/mapbox-gl.js"></script>
    <link href="https://api.tiles.mapbox.com/mapbox-gl-js/v0.50.0/mapbox-gl.css" rel="stylesheet" />

    <link href="https://carto.com/developers/carto-vl/v1.0.0/examples/maps/style.css" rel="stylesheet">
</head>

<body>

    <div id="map"></div>

    <aside class="toolbox">
        <div class="box">
            <header>
                <h1>Rail Accidents by Weather</h1>
            </header>
        </div>
    </aside>

    <script>

        const map = new mapboxgl.Map({
            container: 'map',
            style: carto.basemaps.darkmatter,
            center: [-96, 41],
            zoom: 4,
        });

        carto.setDefaultAuth({
            username: 'cartovl',
            apiKey: 'default_public'
        });

        const source = new carto.source.Dataset("railroad_accidents");

        const viz = new carto.Viz(`
            width: 7
            color: ramp($weather,[darkorange,darkviolet,darkturquoise])
            strokeWidth: 0.2
            strokeColor: black
        `);

        const layer = new carto.Layer('layer', source, viz);

        layer.addTo(map);

    </script>

</body>

</html>

Access data from ramp

To get the necessary information to populate the legend, you use the getLegendData() method. The getLegendData() method needs to reference the ramp expression where the symbology for your map is defined.

Take a look at the point styling for the accidents map. This is the styling that we want to bring into our legend and associate each legend item to each category type that we are symbolizing from the $weather property.

const viz = new carto.Viz(`
    width: 7
    color: ramp($weather, [darkorange, darkviolet, darkturquoise])
    strokeWidth: 0.2
    strokeColor: black
`);

Get legend data

Since the ramp expression is the root expression of a styling property (in this case, color) it can be accessed directly with layer.viz.color.getLegendData().

Note: If styling isn’t tied directly to a styling property, you will need to use a variable. We will explore that method in detail in the Add Widgets guide.

Add the following code to your map right under layer.addTo(map) and before the closing </script> tag. Take a look through the inline comments describing the different steps to place the legend content when a map is loaded.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// A function to convert map colors to HEX values for legend
function rgbToHex(color) {
    return "#" + ((1 << 24) + (color.r << 16) + (color.g << 8) + color.b).toString(16).slice(1);
}

// When layer loads, trigger legend event
layer.on('loaded', () => {

    // Request data for legend from the layer viz
    const colorLegend = layer.viz.color.getLegendData();
    let colorLegendList = '';

    // Create list elements for legend
    colorLegend.data.forEach((legend, index) => {
        const color = rgbToHex(legend.value);

        // Style for legend items
        colorLegendList +=
            `<li><span class="point-mark" style="background-color:${color};border: 1px solid black;"></span> <span>${legend.key}</span></li>\n`;
    });

    // Place list items in the content section of the title/legend box
    document.getElementById('content').innerHTML = colorLegendList;
});

Place legend data

If you load your map after the previous step, you will see that there is still no legend content!

That’s because we have to define a place for the last step of the process (document.getElementById('content').innerHTML = colorLegendList;) to place the information on the map once it is received.

To define where the content should be placed on the map, add a <section> in the title/legend box element of the page:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!-- Add title/legend box -->
<aside class="toolbox">
    <div class="box">
        <header>
            <h1>Rail Accidents by Weather</h1>
        </header>
        <!-- Add legend data -->
        <section>
            <div id="controls">
                <ul id="content"></ul>
            </div>
        </section>
    </div>
</aside>

Now, when you load the map, you will see the complete legend. You will also notice that the CARTO VL interpolated ramp colors (we only provided three colors for six categories) are also brought into the legend with the associated category name.

You can explore the final map here

Overwrite defaults

In the map above, we are symbolizing all six weather categories in the data and therefore have an entry for each type in the legend. There are other times when there is symbology applied to some categories but not all of them. In this case, you will have an “others” legend item.

For example, the map below symbolizes only the top three weather conditions in the rail accident data. The legend labels the top three categories with all other categories labelled as CARTO_VL_OTHERS:

You can overwrite this default label in the style for the colorLegendList with ${legend.key.replace():

1
2
colorLegendList +=
    `<li><span class="point-mark" style="background-color:${color}; border: 1px solid black;"></span><span>${legend.key.replace('CARTO_VL_OTHERS', 'Other weather')}</span></li>\n`;

With that change, the final map will now label other categories as “Other weather” in the legend:

You can explore the final map here

More examples

View the source of the maps below to see how legends work for different map and geometry types.

Choropleth map

You can explore this map here

Categorical lines

You can explore this map here

Image markers

You can explore this map here

Unclassed latitudes

You can explore this map here