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.
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.
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>
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
`);
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;
});
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
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
View the source of the maps below to see how legends work for different map and geometry types.
You can explore this map here
You can explore this map here
You can explore this map here
You can explore this map here