This series of tutorials teaches you how to a create a map to visualize data across a region.
In part 1, you styled US population density data in the Mapbox Studio style editor and published a new style. In part 2, you will make this style come to life with interactions using Mapbox GL JS.
In the last guide, you used the Mapbox Studio style editor to design your map and create a style. But what did the software produce when you clicked Publish?
The style is the most important part of making a web map: it contains all the rules for what features to draw on the webpage and how to draw them. Both Mapbox Studio and Mapbox GL JS interact directly with your style: the Mapbox Studio style editor is a visual interface for creating the style, and Mapbox GL JS is used to add the style to a webpage and interact with it directly by adding and changing the layers and sources in response to browser events.
A map style is a JSON object in the Mapbox Style Specification that contains all the things the browser needs to draw your map correctly. Its main parts are:
sources
: links to all the data that will be styled on the map. When creating a style with the Mapbox Studio style editor, sources are raster and vector tilesets in your Mapbox account.sprite
: a link to all the images and icons that are used in the style.glyphs
: a link to all the fonts that are used in the style.layers
: a list of rules for how the data in sources
should be displayed on the map.When you added the population density data to your Mapbox Studio style in part 1, a link to it was also added to the list of sources in a style object (we normally refer to these as "styles"). Similarly, when you added the population density layer and gave it styling rules for the each category of data, that layer was also added to the list of layers in your style.
Here's what you need to get started:
Open your text editor and create a file called index.html
. Set up the document by adding Mapbox GL JS and its associated CSS file in the header:
<script src="https://api.mapbox.com/mapbox-gl-js/v3.3.0/mapbox-gl.js"></script>
<link
href="https://api.mapbox.com/mapbox-gl-js/v3.3.0/mapbox-gl.css"
rel="stylesheet"
/>
Next, markup the page to create a map container, an information box, and a legend:
<div id="map"></div>
<div class="map-overlay" id="features">
<h2>US population density</h2>
<div id="pd"><p>Hover over a state!</p></div>
</div>
<div class="map-overlay" id="legend"></div>
You will also want to apply some CSS to visualize what the layout looks like. This is particularly important for the map
div, which won't show up on the page until you give it a height
:
body {
margin: 0;
padding: 0;
}
h2,
h3 {
margin: 10px;
font-size: 18px;
}
h3 {
font-size: 16px;
}
p {
margin: 10px;
}
/**
* Create a position for the map
* on the page */
#map {
position: absolute;
top: 0;
bottom: 0;
width: 100%;
}
/**
* Set rules for how the map overlays
* (information box and legend) will be displayed
* on the page. */
.map-overlay {
position: absolute;
bottom: 0;
right: 0;
background: #fff;
margin-right: 20px;
font-family: Arial, sans-serif;
overflow: auto;
border-radius: 3px;
}
#features {
top: 0;
height: 100px;
margin-top: 20px;
width: 250px;
}
#legend {
padding: 10px;
box-shadow: 0 1px 2px rgba(0 0 0 0.1);
line-height: 18px;
height: 150px;
margin-bottom: 40px;
width: 100px;
}
.legend-key {
display: inline-block;
border-radius: 20%;
width: 10px;
height: 10px;
margin-right: 5px;
}
In the next step, you will add the map to your page and the project will start taking shape.
Now that you've added structure to the page, you can start writing some JavaScript! The first thing you'll need to do is add your access token. Without this, the rest of the code will not work. Note: all the following code should be between script
tags.:
mapboxgl.accessToken = 'YOUR_MAPBOX_ACCESS_TOKEN ';
Now that you've added the structure of the page, you can add a map object into the map
div. Be sure to replace your-style-url
with the style URL for the style you created in part 1 of this guide -- otherwise the code won't work!
const map = new mapboxgl.Map({
container: 'map', // container id
style: 'your-style-url' // replace this with your style URL
});
With some projects, this is where you'd stop: you put a map on a page! But for this map, you will add two pieces of additional information that will make the map even more useful: a legend and an information window that shows the population density for whatever state the cursor is hovering on.
Initializing the map on the page does more than create a container in the
map
div. It also tells the browser to request the Mapbox Studio style you
created in part 1. This can take variable amounts of time depending on how
quickly the Mapbox server can respond to that request, and everything else
you're going to add in the code relies on that style being loaded onto the
map. As such, it's important to make sure the style is loaded before any more
code is executed.Fortunately, the map object can tell your browser about
certain events that occur when the map's state changes. One of these events is
load
, which is emitted when the style has been loaded onto the map. Through
the map.on
method, you can make sure that none of the rest of your code is
executed until that event occurs by placing it in a callback
function that is called
when the load
event occurs.
To make sure the rest of the code can execute, it needs to live in a callback function that is executed when the map is finished loading.
map.on('load', () => {
// the rest of the code will go in here
});
Creating a list of the stops you used when styling your layer that contains state data will allow us to add a legend to our map in a later step.
Remember: this code goes inside of the load
callback function!
const layers = [
'0-10',
'10-20',
'20-50',
'50-100',
'100-200',
'200-500',
'500-1000',
'1000+'
];
const colors = [
'#FFEDA0',
'#FED976',
'#FEB24C',
'#FD8D3C',
'#FC4E2A',
'#E31A1C',
'#BD0026',
'#800026'
];
The following code adds a legend to the map. To do so, it iterates through the list of layers you defined above and adds a legend element for each one based on the name of the layer and its color.
// create legend
const legend = document.getElementById('legend');
layers.forEach((layer, i) => {
const color = colors[i];
const item = document.createElement('div');
const key = document.createElement('span');
key.className = 'legend-key';
key.style.backgroundColor = color;
const value = document.createElement('span');
value.innerHTML = `${layer}`;
item.appendChild(key);
item.appendChild(value);
legend.appendChild(item);
});
When the cursor is hovering over a state, the information window should show the population density information for that state. If the cursor is not hovering over a state, the information window should say, "Hover over a state!"
To do this, add a listener for the mousemove
event, identify which state is at the location of the cursor if any, and update the information window:
map.on('mousemove', (event) => {
const states = map.queryRenderedFeatures(event.point, {
layers: ['statedata']
});
document.getElementById('pd').innerHTML = states.length
? `<h3>${states[0].properties.name}</h3><p><strong><em>${states[0].properties.density}</strong> people per square mile</em></p>`
: `<p>Hover over a state!</p>`;
});
Almost done! A couple last little steps:
Add a single line of code to give the map the default pointer cursor.
map.getCanvas().style.cursor = 'default';
Make sure the map shows the continental U.S. when it's loaded by setting the bounds of the map on load:
map.fitBounds([
[-133.2421875, 16.972741],
[-47.63671875, 52.696361]
]);
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Demo: Make a choropleth map</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<script src="https://api.tiles.mapbox.com/mapbox-gl-js/v3.3.0/mapbox-gl.js"></script>
<link
href="https://api.tiles.mapbox.com/mapbox-gl-js/v3.3.0/mapbox-gl.css"
rel="stylesheet"
/>
<style>
body {
margin: 0;
padding: 0;
}
h2,
h3 {
margin: 10px;
font-size: 18px;
}
h3 {
font-size: 16px;
}
p {
margin: 10px;
}
.map-overlay {
position: absolute;
bottom: 0;
right: 0;
background: #fff;
margin-right: 20px;
font-family: Arial, sans-serif;
overflow: auto;
border-radius: 3px;
}
#map {
position: absolute;
top: 0;
bottom: 0;
width: 100%;
}
#features {
top: 0;
height: 100px;
margin-top: 20px;
width: 250px;
}
#legend {
padding: 10px;
box-shadow: 0 1px 2px rgb(0 0 0 / 10%);
line-height: 18px;
height: 150px;
margin-bottom: 40px;
width: 100px;
}
.legend-key {
display: inline-block;
border-radius: 20%;
width: 10px;
height: 10px;
margin-right: 5px;
}
</style>
</head>
<body>
<div id="map"></div>
<div class="map-overlay" id="features">
<h2>US population density</h2>
<div id="pd"><p>Hover over a state!</p></div>
</div>
<div class="map-overlay" id="legend"></div>
<script>
// define access token
mapboxgl.accessToken = '{{MAPBOX_ACCESS_TOKEN}}';
// create map
const map = new mapboxgl.Map({
container: 'map', // container id
style: 'mapbox://styles/examples/cjgioozof002u2sr5k7t14dim' // map style URL from Mapbox Studio
});
// wait for map to load before adjusting it
map.on('load', () => {
// make a pointer cursor
map.getCanvas().style.cursor = 'default';
// set map bounds to the continental US
map.fitBounds([
[-133.2421875, 16.972741],
[-47.63671875, 52.696361]
]);
// define layer names
const layers = [
'0-10',
'10-20',
'20-50',
'50-100',
'100-200',
'200-500',
'500-1000',
'1000+'
];
const colors = [
'#FFEDA0',
'#FED976',
'#FEB24C',
'#FD8D3C',
'#FC4E2A',
'#E31A1C',
'#BD0026',
'#800026'
];
// create legend
const legend = document.getElementById('legend');
layers.forEach((layer, i) => {
const color = colors[i];
const item = document.createElement('div');
const key = document.createElement('span');
key.className = 'legend-key';
key.style.backgroundColor = color;
const value = document.createElement('span');
value.innerHTML = ``;
item.appendChild(key);
item.appendChild(value);
legend.appendChild(item);
});
// change info window on hover
map.on('mousemove', (event) => {
const states = map.queryRenderedFeatures(event.point, {
layers: ['statedata']
});
document.getElementById('pd').innerHTML = states.length
? `<h3>${states[0].properties.name}</h3><p><strong><em>${states[0].properties.density}</strong> people per square mile</em></p>`
: `<p>Hover over a state!</p>`;
});
});
</script>
</body>
</html>
Nice job! For more things you can do with Mapbox Studio, explore the Mapbox Studio Manual. For more information on Mapbox GL JS and how it works, read the How web apps work guide.
Congratulations! You have created an interactive choropleth map!
queryRenderedFeatures
to retrieve map datafitBounds
If you are interested in data visualization see our tutorial Create a map with a data visualization component