Grid Section Map Overlay (Leaflet)


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 Leaflet. We also have tutorials demonstrating its use with both Mapbox and Google Maps.

Step 1: Obtain a API keys

Sign up to obtain your free what3words API key.

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 Leaflet API, for example:

<script src="https://unpkg.com/leaflet@1.5.1/dist/leaflet.js" integrity="sha512-GffPMF3RvMeYyc1LWMHtK8EbPv0iNZ8/oTtHPx9/cc2ILxQ+u905qIwdpULaqDkyBKgOaB57QTMg7ztg8Jm2Og==" crossorigin=""></script>

Step 3: Map Style

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

<link rel="stylesheet" href="https://unpkg.com/leaflet@1.5.1/dist/leaflet.css" integrity="sha512-xwE/Az9zrjBIphAcBb3F6JVqxf46+CDLwfLMHloNu6KEQCAWi6HcDUbeOfBIptF7tcCzusKFjFw2yuvEpDL9wQ==" crossorigin="" />
<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 Leaflet JavaScript API.

<div id="map"></div>
<script>
  var map = L.map('map').setView([51.505, -0.09], 19);
  L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
      attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
      maxNativeZoom:19,
      maxZoom:25
  }).addTo(map);
</script>

Step 5: Grid Overlay

The what3words grid can be displayed on top of a Leaflet Map adding a GeoJSON layer to the map.

Because only a segment of grid section can be requested, we need to obtain a new grid section each time the map 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) {
          // If the grid layer is already present, remove it as it will need to be replaced by the new grid section
          if (typeof grid_layer !== 'undefined') {
            map.removeLayer(grid_layer);
          }

          // Create a new GeoJSON layer, based on the GeoJSON returned from the what3words API
          grid_layer = L.geoJSON(data, {
            style: function() {
              return {
                color: '#777',
                stroke: true,
                weight: 0.5
              };
            }
          }).addTo(map);
        }).catch(console.error);
    } else {
      // If the grid layer already exists, remove it as the zoom level no longer requires the grid to be displayed
      if (typeof grid_layer !== 'undefined') {
        map.removeLayer(grid_layer);
      }
    }
  }

  map.whenReady(drawGrid);
  map.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>

    <link rel="stylesheet" href="https://unpkg.com/leaflet@1.5.1/dist/leaflet.css" integrity="sha512-xwE/Az9zrjBIphAcBb3F6JVqxf46+CDLwfLMHloNu6KEQCAWi6HcDUbeOfBIptF7tcCzusKFjFw2yuvEpDL9wQ==" crossorigin="" />
    <script src="https://unpkg.com/leaflet@1.5.1/dist/leaflet.js" integrity="sha512-GffPMF3RvMeYyc1LWMHtK8EbPv0iNZ8/oTtHPx9/cc2ILxQ+u905qIwdpULaqDkyBKgOaB57QTMg7ztg8Jm2Og==" crossorigin=""></script>

    <style>
      #map {
        height: 100%;
      }

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

  <body>
    <div id="map"></div>
    <script>
      var map = L.map('map').setView([51.505, -0.09], 19);
      L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
        attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
        maxNativeZoom: 19,
        maxZoom: 25
      }).addTo(map);

    </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) {
              // If the grid layer is already present, remove it as it will need to be replaced by the new grid section
              if (typeof grid_layer !== 'undefined') {
                map.removeLayer(grid_layer);
              }

              // Create a new GeoJSON layer, based on the GeoJSON returned from the what3words API
              grid_layer = L.geoJSON(data, {
                style: function() {
                  return {
                    color: '#777',
                    stroke: true,
                    weight: 0.5
                  };
                }
              }).addTo(map);
            }).catch(console.error);
        } else {
          // If the grid layer already exists, remove it as the zoom level no longer requires the grid to be displayed
          if (typeof grid_layer !== 'undefined') {
            map.removeLayer(grid_layer);
          }
        }
      }

      map.whenReady(drawGrid);
      map.on('move', drawGrid);
    </script>
  </body>
</html>