Grid Section Map Overlay (Mapbox)


The what3words REST API supports the abilty to obtain a section of the 3m x 3m what3words grid in GeoJSON format, making it very simple to display on a map.

This tutorial demostrates integrating the what3words grid with Mapbox. We also have tutorials demonstrating its use with both Google Maps and Leaflet.

Step 1: Obtain a API keys

Sign up to obtain your free what3words API key.

To display the grid with Mapbox, you will also need to obtain a Mapbox access token.

Step 2: Installation

To load the what3words JavaScript API, use a script tag like the one in the following example:

<script src="https://assets.what3words.com/sdk/v3/what3words.js?key=YOUR_API_KEY"></script>

The key parameter contains your application's API key. Sign up to obtain your free API key.

You will also need to load the Mapbox API, for example:

<script src="https://api.tiles.mapbox.com/mapbox-gl-js/v0.53.0/mapbox-gl.js"></script>

Step 3: Map Style

Within the head tag, we need to set style properties for the Mapbox. In this example, we'll set the map to display full screen.

<link href="https://api.tiles.mapbox.com/mapbox-gl-js/v0.53.0/mapbox-gl.css" rel="stylesheet" />
<style>
  #map {
    height: 100%;
  }

  html, body {
    height: 100%;
    margin: 0;
    padding: 0;
  }
</style>

Step 4: Map Initialisation

Within the body tag, we need to create a new div element to contain the map component, as well as initialise it using the Mapbox JavaScript API.

<div id="map"></div>
<script>
  // Create the MapBox
  mapboxgl.accessToken = YOUR_MAPBOX_ACCESS_TOKEN;
  let map = new mapboxgl.Map({
    container: "map", // container id
    style: "mapbox://styles/mapbox/streets-v9", // stylesheet location
    center: [-0.195499, 51.52086], // starting position [lng, lat]
    zoom: 18 // starting zoom
  });
  map.addControl(new mapboxgl.NavigationControl());
</script>

Step 5: Grid Overlay

The what3words grid can be displayed as a Mapbox layer by creating a GeoJSON data source, and attaching it to a new layer.

Because only a segment of grid section can be requested, we need to obtain a new grid section each time the map loaded, or is panned or zoomed.

<script>
  function drawGrid() {
    const zoom = map.getZoom();
    const loadFeatures = zoom > 17;

    if (loadFeatures) { // Zoom level is high enough
      var ne = map.getBounds().getNorthEast();
      var sw = map.getBounds().getSouthWest();

      // Call the what3words Grid API to obtain the grid squares within the current visble bounding box
      what3words.api
        .gridSectionGeoJson({
          southwest: {
            lat: sw.lat, lng: sw.lng
          },
          northeast: {
            lat: ne.lat, lng: ne.lng
          }
        }).then(function(data) {
          // Get the grid source from the map (it won't exist initally)
          var grid = map.getSource('grid');

          if (typeof grid === 'undefined') {
            // Create a source of type 'geojson' which loads the GeoJSON returned from the what3words API
            map.addSource('grid', {
              type: 'geojson',
              data: data
            });

            // Create a new layer, which loads data from the newly created data source
            map.addLayer({
              id: 'grid_layer',
              type: "line",
              source: 'grid',
              layout: {
                "line-join": "round",
                "line-cap": "round"
              },
              paint: {
                "line-color": '#777',
                "line-width": .5
              }
            });
          } else {
            // The source and map layer already exist, so just update the source data to be the new
            // GeoJSON returned from the what3words API
            map.getSource('grid').setData(data);
          }
        }).catch(console.error);
    }

    // If we have reached the required zoom level, set the 'grid_layer' to be visible
    var grid_layer = map.getLayer('grid_layer');
    if (typeof grid_layer !== 'undefined') {
      map.setLayoutProperty('grid_layer', 'visibility', loadFeatures ? 'visible' : 'none');
    }
  }

  // When the map is either loaded or moved, check to see if the grid should be draw
  // if the appropriate zoom level has been met, and if so draw it on.
  map
    .on('load', drawGrid)
    .on('move', drawGrid);
</script>

Step 6: Putting It All Together

The example below takes the steps listed above, and puts them into a complete, working example.

<html>
  <head>
    <script src="https://assets.what3words.com/sdk/v3/what3words.js?key=YOUR_API_KEY"></script>
    <script src="https://api.tiles.mapbox.com/mapbox-gl-js/v0.53.0/mapbox-gl.js"></script>

    <link href="https://api.tiles.mapbox.com/mapbox-gl-js/v0.53.0/mapbox-gl.css" rel="stylesheet" />
    <style>
      #map {
        height: 100%;
      }

      html, body {
        height: 100%;
        margin: 0;
        padding: 0;
      }
    </style>
  </head>

  <body>
    <div id="map"></div>
    <script>
      // Create the Mapbox
      mapboxgl.accessToken = "YOUR_MAPBOX_TOKEN";
      let map = new mapboxgl.Map({
        container: "map", // container id
        style: "mapbox://styles/mapbox/streets-v9", // stylesheet location
        center: [-0.195499, 51.52086], // starting position [lng, lat]
        zoom: 18 // starting zoom
      });
      map.addControl(new mapboxgl.NavigationControl());
    </script>
    <script>
      function drawGrid() {
        const zoom = map.getZoom();
        const loadFeatures = zoom > 17;

        if (loadFeatures) { // Zoom level is high enough
          var ne = map.getBounds().getNorthEast();
          var sw = map.getBounds().getSouthWest();

          // Call the what3words Grid API to obtain the grid squares within the current visble bounding box
          what3words.api
            .gridSectionGeoJson({
              southwest: {
                lat: sw.lat, lng: sw.lng
              },
              northeast: {
                lat: ne.lat, lng: ne.lng
              }
            }).then(function(data) {
              // Get the grid source from the map (it won't exist initally)
              var grid = map.getSource('grid');

              if (grid === undefined) {
                // Create a source of type 'geojson' which loads the GeoJSON returned from the what3words API
                map.addSource('grid', {
                  type: 'geojson',
                  data: data
                });

                // Create a new layer, which loads data from the newly created data source
                map.addLayer({
                  id: 'grid_layer',
                  type: "line",
                  source: 'grid',
                  layout: {
                    "line-join": "round",
                    "line-cap": "round"
                  },
                  paint: {
                    "line-color": '#777',
                    "line-width": .5
                  }
                });
              } else {
                // The source and map layer already exist, so just update the source data to be the new
                // GeoJSON returned from the what3words API
                map.getSource('grid').setData(data);
              }
            }).catch(console.error);
        }

        // If we have reached the required zoom level, set the 'grid_layer' to be visible
        var grid_layer = map.getLayer('grid_layer');
        if (typeof grid_layer !== 'undefined') {
          map.setLayoutProperty('grid_layer', 'visibility', loadFeatures ? 'visible' : 'none');
        }
      }

      // When the map is either loaded or moved, check to see if the grid should be draw
      // if the appropriate zoom level has been met, and if so draw it on.
      map
        .on('load', drawGrid)
        .on('move', drawGrid);
    </script>
  </body>
</html>