Toggle Layers with Mapbox GL JS
Asked Answered
V

2

6

I'd like to toggle between layers in Mapbox GL JS. I've created some radio buttons that partly work in that you can switch from one layer to the other but not back or forth.

Below is my code for toggling the layers so far:

    var layerList = document.getElementById('toggle');
    var inputs = layerList.getElementsByTagName('input');

    function switchLayer(layer) {
        var layerId = layer.target.id;
        map.setLayoutProperty(layerId, 'visibility');
    }

    for (var i = 0; i < inputs.length; i++) {
    inputs[i].onclick = switchLayer;
    }

Here's the rest of the code (simplified) and a link to jsbin https://jsbin.com/cigekutiho/edit?html,output

   <!DOCTYPE html>
   <html>
   <head>
   <meta charset=utf-8 />
   <title>Test</title>
   <meta name='viewport' content='initial-scale=1,maximum-scale=1,user-scalable=no' />
   <link rel="stylesheet" type="text/css" href="css/style.css" />
   <script src='https://api.mapbox.com/mapbox.js/v3.1.1/mapbox.js'> 
   </script>
   <link href='https://api.mapbox.com/mapbox.js/v3.1.1/mapbox.css' rel='stylesheet' />
   <script src='https://api.mapbox.com/mapbox-gl-js/v0.44.1/mapbox-gl.js'></script>
   <link href='https://api.mapbox.com/mapbox-gl-js/v0.44.1/mapbox-gl.css' rel='stylesheet'/>
   <script src='https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-geocoder/v2.2.0/mapbox-gl-geocoder.min.js'></script>
   <link rel='stylesheet' href='https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-geocoder/v2.2.0/mapbox-gl-geocoder.css' type='text/css' />
   <link href='https://www.mapbox.com/base/latest/base.css' rel='stylesheet'/>
   <style>
   body { 
      margin: 0; 
      padding: 0;
   }

   #map { 
      position: absolute; 
      top: 0; 
      bottom: 0; 
      width: 100%; 
   }

   #toggle {
   position: absolute;
   padding: 10px;
   background: #dddddd;
   font-family: Helvetica;
  }

  .rounded-toggle {
  position: absolute;
  border: 1px solid rgba(0,0,0,0.4);
  font-family: Helvetica;
  width: 200px;
  height: 47px;
  text-align: center;
  margin-left: 550px;
  top: 10px;
  cursor: pointer;
  }
  </style>
  </head>

  <body>

  <div id='map'></div>
  <div class='rounded-toggle inline' id='toggle'>
     <input id='Test_A' type='radio' name='rtoggle' value='Test_A' checked='checked'>
     <label for='Test_A'>Test A</label>
     <input id='Test_B' type='radio' name='rtoggle' value='Test_B'>
     <label for='Test_B'>Test B</label>
  </div>

  <script>
mapboxgl.accessToken = 'pk.eyJ1IjoibGlhbWJvbHRvbnVrIiwiYSI6IjJyeWxEMzgifQ.OROtY7TDOwNOmAOfCZeo4w';

var map_zoom = L.mapbox.map('map');
map_zoom.removeControl(map_zoom.zoomControl);

var map = new mapboxgl.Map({
    container: 'map',
    style: 'mapbox://styles/liamboltonuk/cjfykyscf6i272snyorquvam6',
    attributionControl: false,
    minZoom: 13,
    center: [-0.042582, 51.518039]
});

map.on('load', function () {
map.addSource('Test_A', {
        type: 'vector',
        url: 'mapbox://liamboltonuk.bv937ecm'
    });
map.addLayer({
        'id': 'Test_A',
        'type': 'circle',
        'source': 'Test_A',
        'layout': 
        {
        'visibility': 'visible'
        },
        'paint': {
            'circle-radius': 4,
            'circle-color': {
            property: 'conct',
            type: 'interval',
            stops: [
                [1, '#00477a'],
                [700, '#00477a']
            ]
            },
        'circle-opacity': {
            stops: [
                [12, 1],
                [13, 0.75]
            ] 
        },
    },
        'source-layer': 'LAEI_2013_N0x-5pzq5d'
});
});

map.on('load', function () {
map.addSource('Test_B', {
        type: 'vector',
        url: 'mapbox://liamboltonuk.bv937ecm'
    });
map.addLayer({
        'id': 'Test_B',
        'type': 'circle',
        'source': 'Test_B',
        'layout': 
        {
        'visibility': 'none'
        },
        'paint': {
            'circle-radius': 4,
            'circle-color': {
            property: 'conct',
            type: 'interval',
            stops: [
                [1, '#66e8ff'],
                [700, '#66e8ff']
            ]
            },
        'circle-opacity': {
            stops: [
                [12, 1],
                [13, 0.75]
            ] 
        },
    },
        'source-layer': 'LAEI_2013_N0x-5pzq5d'
});
});

    var layerList = document.getElementById('toggle');
    var inputs = layerList.getElementsByTagName('input');

    function switchLayer(layer) {
      var layerId = layer.target.id;
      map.setLayoutProperty(layerId, 'visibility');
    }

    for (var i = 0; i < inputs.length; i++) {
      inputs[i].onclick = switchLayer;
    }

    </script>
    </body>
    </html>
Volscian answered 20/4, 2018 at 19:21 Comment(0)
C
11

Map.setLayoutProperty takes 3 arguments (layer, name, value). So to make the layer invisible use:

map.setLayoutProperty('my-layer', 'visibility', 'none');

then to make it visible again use:

map.setLayoutProperty('my-layer', 'visibility', 'visible');
Crippen answered 21/4, 2018 at 4:55 Comment(2)
Could you show how that would work and solve the problem using the code provided?Volscian
Where you currently set the visibility of the layer, you need to use a 3rd argument for the value. So you could track this state somewhere and use that, or getLayoutProperty and flip it that way.Crippen
H
4

Mapbox has some very nice starter code for doing this. See the docs or play with the actual code here

<!DOCTYPE html>
<html>
<head>
    <meta charset='utf-8' />
    <title>Show and hide layers</title>
    <meta name='viewport' content='initial-scale=1,maximum-scale=1,user-scalable=no' />
    <script src='https://api.tiles.mapbox.com/mapbox-gl-js/v0.53.1/mapbox-gl.js'></script>
    <link href='https://api.tiles.mapbox.com/mapbox-gl-js/v0.53.1/mapbox-gl.css' rel='stylesheet' />
    <style>
        body { margin:0; padding:0; }
        #map { position:absolute; top:0; bottom:0; width:100%; }
    </style>
</head>
<body>

<style>
    #menu {
        background: #fff;
        position: absolute;
        z-index: 1;
        top: 10px;
        right: 10px;
        border-radius: 3px;
        width: 120px;
        border: 1px solid rgba(0,0,0,0.4);
        font-family: 'Open Sans', sans-serif;
    }

    #menu a {
        font-size: 13px;
        color: #404040;
        display: block;
        margin: 0;
        padding: 0;
        padding: 10px;
        text-decoration: none;
        border-bottom: 1px solid rgba(0,0,0,0.25);
        text-align: center;
    }

    #menu a:last-child {
        border: none;
    }

    #menu a:hover {
        background-color: #f8f8f8;
        color: #404040;
    }

    #menu a.active {
        background-color: #3887be;
        color: #ffffff;
    }

    #menu a.active:hover {
        background: #3074a4;
    }
</style>

<nav id="menu"></nav>
<div id="map"></div>

<script>
mapboxgl.accessToken = 'pk.eyJ1IjoiYWFnb3N0aW5pLXBlb3JpYWdvdiIsImEiOiJjanRnMzdjYnkwcDAxM3ltcDNjaXkyNWgzIn0.NhV8nl5x6256q8F_-gVCtQ';
var map = new mapboxgl.Map({
    container: 'map',
    style: 'mapbox://styles/mapbox/streets-v11',
    zoom: 15,
    center: [-71.97722138410576, -13.517379300798098]
});

map.on('load', function () {
    map.addSource('museums', {
        type: 'vector',
        url: 'mapbox://mapbox.2opop9hr'
    });
    map.addLayer({
        'id': 'museums',
        'type': 'circle',
        'source': 'museums',
        'layout': {
            'visibility': 'visible'
        },
        'paint': {
            'circle-radius': 8,
            'circle-color': 'rgba(55,148,179,1)'
        },
        'source-layer': 'museum-cusco'
    });

    map.addSource('contours', {
        type: 'vector',
        url: 'mapbox://mapbox.mapbox-terrain-v2'
    });
    map.addLayer({
        'id': 'contours',
        'type': 'line',
        'source': 'contours',
        'source-layer': 'contour',
        'layout': {
            'visibility': 'visible',
            'line-join': 'round',
            'line-cap': 'round'
        },
        'paint': {
            'line-color': '#877b59',
            'line-width': 1
        }
    });
});

var toggleableLayerIds = [ 'contours', 'museums' ];

for (var i = 0; i < toggleableLayerIds.length; i++) {
    var id = toggleableLayerIds[i];

    var link = document.createElement('a');
    link.href = '#';
    link.className = 'active';
    link.textContent = id;

    link.onclick = function (e) {
        var clickedLayer = this.textContent;
        e.preventDefault();
        e.stopPropagation();

        var visibility = map.getLayoutProperty(clickedLayer, 'visibility');

        if (visibility === 'visible') {
            map.setLayoutProperty(clickedLayer, 'visibility', 'none');
            this.className = '';
        } else {
            this.className = 'active';
            map.setLayoutProperty(clickedLayer, 'visibility', 'visible');
        }
    };

    var layers = document.getElementById('menu');
    layers.appendChild(link);
}

</script>

</body>
</html>
Hadsall answered 3/4, 2019 at 15:12 Comment(1)
The Mapbox example is nice for switching between 2 layers. But that wouldn't be a good solution if you wanted 20 layers and layer groups/layer nesting. Any ideas on a more scalable solution?Shinbone

© 2022 - 2024 — McMap. All rights reserved.