DEV Community

Cover image for Building a Country Highlighting Tool With Mapbox
Conlin Durbin
Conlin Durbin

Posted on • Originally published at wuz.sh on

Building a Country Highlighting Tool With Mapbox

This is an old post I recently republished on my newly built personal site. Thanks for reading!

For a recent project, we needed to create a dynamic map that highlighted the areas in which our client had done work. After evaluating the requirements and looking into our options, we decided to build this using Mapbox. Their integration with OpenStreetMap and ability to easily customize the tilesets and style on our map was an instant hook.

Let’s see what we’re building.

In this example, we are going to create a pop up that shows some basic stats about the country. This method can be used for any kind of Mapbox tileset, but we're using data from Natural Earth Data. Below is a link for the vector tileset we are using. Click the download countries link on that page and let’s get started!

getting your access token

To begin building, you’ll need to create a Mapbox Studio account. Once you get signed up, you’ll need to get your API access token. Login to your Mapboxaccount and click into the Account button on the bottom left of the dashboard. Then on API access tokens on the top right. Look for your Default Public Token.

The next couple steps assume that you are using local files to run this code. If you want to play around with it in Codepen, here is a quick templatethat has the start of the project.

Go ahead and copy then paste it into a new Javascript file named main.js like this:

mapboxgl.accessToken =
  'pk.eyJ1IjoiYnlmcm9zdC1hcnRpY2xlcyIsImEiOiJjajVsZ3NwZGczMWNtMnFyeTR2cHRnajZ4In0.HOjYrueiLWlhLfhsDCa7wQ'; // Replace with your token
Enter fullscreen mode Exit fullscreen mode

Now we just need some basic HTML boilerplate. Create a file called index.html and add the following:

<!DOCTYPE html>
<html>
  <head>
    <title>My Awesome Map</title>
    <link rel="stylesheet" href="style.css" />
  </head>
  <body>
    <div id="map"></div>
    <script src="https://api.mapbox.com/mapbox-gl-js/v0.38.0/mapbox-gl.js"></script>
    <script src="main.js"></script>
  </body>
</html>
Enter fullscreen mode Exit fullscreen mode

Now let’s add some styles. Create a style.css and add this:

#map {
  height: 500px;
  width: 1000px;
}
#map .mapboxgl-popup-content {
  padding: 10px;
  max-width: 300px;
  padding-top: 20px;
}
#map .mapboxgl-popup-content ul {
  list-style: none;
  margin: 0;
  padding: 0;
  float: left;
}

#map .mapboxgl-popup-content ul h3 {
  margin: 0 0 10px 0;
}

#map .mapboxgl-popup-content img {
  float: left;
  width: 30px;
  margin-right: 10px;
}
Enter fullscreen mode Exit fullscreen mode

If you load up your page, you probably won’t see anything yet. Our next step is to add a map. Add the following code to main.js:

mapboxgl.accessToken =
  'pk.eyJ1IjoiYnlmcm9zdC1hcnRpY2xlcyIsImEiOiJjajVsZ3NwZGczMWNtMnFyeTR2cHRnajZ4In0.HOjYrueiLWlhLfhsDCa7wQ'; // Replace with your token

var map = new mapboxgl.Map({
  container: 'map', //this is the id of the container you want your map in
  style: 'mapbox://styles/mapbox/light-v9', // this controls the style of the map. Want to see more? Try changing 'light' to 'simple'.
  minZoom: 2, // We want our map to start out pretty zoomed in to start.
});
Enter fullscreen mode Exit fullscreen mode

If you used the Codepen template from above, you should already have everything up to this point.

At this point, we need to load in our custom tileset in Mapbox Studio. If you haven’t already, download the Natural Earth Data from earlier. Open Mapbox Studio and click on Tilesets. From there click on “New tileset”

the new tileset button

Upload the entire zip file you downloaded from Natural Earth Data.

uploading the tile set

Wait for it to finish uploading and processing.

waiting for the upload to finish

Once it’s done, click into your custom tileset.

your custom tileset

We need to load this custom tileset into your map now. We create a function on map load. Create a custom event listener and call addLayer inside it.

mapid

Copy your map ID

Look for the Map ID on the right hand side bar.

copyname

Copy the name starting with ne_

You’ll also need the source layer name, which is that bit starting with ne_. Grab this and the Map ID and add them to your JS code.

mapboxgl.accessToken =
  'pk.eyJ1IjoiYnlmcm9zdC1hcnRpY2xlcyIsImEiOiJjajVsZ3NwZGczMWNtMnFyeTR2cHRnajZ4In0.HOjYrueiLWlhLfhsDCa7wQ'; // Replace with your token

var map = new mapboxgl.Map({
  container: 'map', //this is the id of the container you want your map in
  style: 'mapbox://styles/mapbox/light-v9', // this controls the style of the map. Want to see more? Try changing 'light' to 'simple'.
  minZoom: 2, // We want our map to start out pretty zoomed in to start.
});

map.on('load', function() {
  //On map load, we want to do some stuff
  map.addLayer({
    //here we are adding a layer containing the tileset we just uploaded
    id: 'countries', //this is the name of our layer, which we will need later
    source: {
      type: 'vector',
      url: 'mapbox://', // <--- Add the Map ID you copied here
    },
    'source-layer': '', // <--- Add the source layer name you copied here
    type: 'fill',
    paint: {
      'fill-color': '#52489C', //this is the color you want your tileset to have (I used a nice purple color)
      'fill-outline-color': '#F2F2F2', //this helps us distinguish individual countries a bit better by giving them an outline
    },
  });
});
Enter fullscreen mode Exit fullscreen mode

We should now have loaded the tileset and your map should look something like this:

Right now this isn’t super helpful. All of the countries are showing, which makes it hard to distinguish anything. Let’s filter the data a bit.

For this, we want to filter by ISO Alpha3 Codes, which exist in our tileset under the ID “ADM0_A3_IS”.

We add a line to the load function to start filtering:

mapboxgl.accessToken =
  'pk.eyJ1IjoiYnlmcm9zdC1hcnRpY2xlcyIsImEiOiJjajVsZ3NwZGczMWNtMnFyeTR2cHRnajZ4In0.HOjYrueiLWlhLfhsDCa7wQ'; // Replace with your token

var map = new mapboxgl.Map({
  container: 'map', //this is the id of the container you want your map in
  style: 'mapbox://styles/mapbox/light-v9', // this controls the style of the map. Want to see more? Try changing 'light' to 'simple'.
  minZoom: 2, // We want our map to start out pretty zoomed in to start.
});

map.on('load', function() {
  //On map load, we want to do some stuff
  map.addLayer({
    //here we are adding a layer containing the tileset we just uploaded
    id: 'countries', //this is the name of our layer, which we will need later
    source: {
      type: 'vector',
      url: 'mapbox://', // <--- Add the Map ID you copied here
    },
    'source-layer': '', // <--- Add the source layer name you copied here
    type: 'fill',
    paint: {
      'fill-color': '#52489C', //this is the color you want your tileset to have (I used a nice purple color)
      'fill-outline-color': '#F2F2F2', //this helps us distinguish individual countries a bit better by giving them an outline
    },
  });

  map.setFilter(
    'countries',
    ['in', 'ADM0_A3_IS'].concat(['USA', 'AUS', 'NGA']),
  ); // This line lets us filter by country codes.
});
Enter fullscreen mode Exit fullscreen mode

Much better. Now our map highlights three countries: Nigeria, Australia, and the USA. If we want to add more countries, we can just edit the array of country codes. We could even pull these codes from an API and add them that way.

Finally, let’s make the map interactive. For this, we are going to use the API provided by REST Countries. Luckily, they have an endpoint that accepts ISO Alpha3 codes!

mapboxgl.accessToken =
  'pk.eyJ1IjoiYnlmcm9zdC1hcnRpY2xlcyIsImEiOiJjajVsZ3NwZGczMWNtMnFyeTR2cHRnajZ4In0.HOjYrueiLWlhLfhsDCa7wQ'; // Replace with your token

var map = new mapboxgl.Map({
  container: 'map', //this is the id of the container you want your map in
  style: 'mapbox://styles/mapbox/light-v9', // this controls the style of the map. Want to see more? Try changing 'light' to 'simple'.
  minZoom: 2, // We want our map to start out pretty zoomed in to start.
});

map.on('load', function() {
  //On map load, we want to do some stuff
  map.addLayer({
    //here we are adding a layer containing the tileset we just uploaded
    id: 'countries', //this is the name of our layer, which we will need later
    source: {
      type: 'vector',
      url: 'mapbox://byfrost-articles.74qv0xp0', // <--- Add the Map ID you copied here
    },
    'source-layer': 'ne_10m_admin_0_countries-76t9ly', // <--- Add the source layer name you copied here
    type: 'fill',
    paint: {
      'fill-color': '#52489C', //this is the color you want your tileset to have (I used a nice purple color)
      'fill-outline-color': '#F2F2F2', //this helps us distinguish individual countries a bit better by giving them an outline
    },
  });

  map.setFilter(
    'countries',
    ['in', 'ADM0_A3_IS'].concat(['USA', 'AUS', 'NGA']),
  ); // This line lets us filter by country codes.

  map.on('click', 'countries', function(mapElement) {
    const countryCode = mapElement.features[0].properties.ADM0_A3_IS; // Grab the country code from the map properties.

    fetch(`https://restcountries.eu/rest/v2/alpha/${countryCode}`) // Using tempalate tags to create the API request
      .then(data => data.json()) //fetch returns an object with a .json() method, which returns a promise
      .then(country => {
        //country contains the data from the API request
        // Let's build our HTML in a template tag
        const html = ` 
        <img src='${country.flag}' /> 
        <ul>
          <li><h3>${country.name}</h3></li>
          <li><strong>Currencies:</strong> ${country.currencies
            .map(c => c.code)
            .join(', ')}</li>
          <li><strong>Capital:</strong> ${country.capital}</li>
          <li><strong>Population:</strong> ${country.population}</li>
          <li><strong>Demonym:</strong> ${country.demonym}</li>
        </ul>
      `; // Now we have a good looking popup HTML segment.
        new mapboxgl.Popup() //Create a new popup
          .setLngLat(mapElement.lngLat) // Set where we want it to appear (where we clicked)
          .setHTML(html) // Add the HTML we just made to the popup
          .addTo(map); // Add the popup to the map
      });
  });
});
Enter fullscreen mode Exit fullscreen mode

Now we have an interactive map with highlighted countries!


Thanks for reading! I just started a new newsletter and I'd love fo you to read sign up for it! I'll be sharing a weekly update with some articles I've written, something cool I've found, and a few of my favorite links from around the web.

🎉 Join the newsletter 🎉


Top comments (2)

Collapse
 
techcater profile image
Tech Cater

Thanks for the article. How about different colors for different countries?

Collapse
 
kaleidawave profile image
Ben

Anyone else having issues with the layers rendering over the labels?