Leaflet – Make Data Layer Disappear and Another Appear on Click

javascriptlayersleaflet

I'm mapping COVID vaccinations per state and per county in the US. I have state data as well as county data displaying properly.

I'm trying to make it so that when I click on a state it zooms in to the selected state, turns off the state data layer and turns on the county data layer. So basically it goes from showing state data to county data.

How do I do this? Would I need to add something to the zoomTofeature function?

    var map = L.map("map", {
    center: [39.5, -95.3],
    zoom: 3.5,
  });

  // add basemap
  var Stamen_TonerLite = L.tileLayer(
    "https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png",
    {
      attribution:
        '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors | &copy; <a href="https://carto.com/attributions">CARTO</a> | <a href="https://data.cdc.gov/Vaccinations/COVID-19-Vaccinations-in-the-United-States-County/8xkx-amqh/data">County data</a> | <a href="https://data.cdc.gov/Vaccinations/COVID-19-Vaccinations-in-the-United-States-Jurisdi/unsk-b7fc/data">State data</a> | Map: <a href="https://weircf.wixsite.com/e-portfolio">Chip Weir</a>',
      subdomains: "abcd",
      maxZoom: 19,
    }
  ).addTo(map);

  //zoom to a state, turn off state data, turn on county data
  function zoomToFeature(e) {
    map.fitBounds(e.target.getBounds());
  }

  //zoom to county
  function zoomToFeature1(e) {
    map.fitBounds(e.target.getBounds());
  }

  function highlightFeature(e) {
    var layer = e.target;
    layer.setStyle({
      weight: 4,
      opacity: 1,
      color: "#dbff4d",
    });
    layer.bringToFront();
  }

  function highlightFeature1(e) {
    var layer = e.target;
    layer.setStyle({
      weight: 4,
      opacity: 1,
      color: "#dbff4d",
    });
    layer.bringToFront();
  }

  //reset the hightlighted states on mouseout
  function resetHighlight(e) {
    geojsonStates.resetStyle(e.target);
  }

  //reset the hightlighted counties on mouseout
  function resetHighlight1(e) {
    geojsonCounties.resetStyle(e.target);
  }

  //add these events to the layer object
  function onEachFeature(feature, layer) {
    layer.on({
      mouseover: highlightFeature,
      click: zoomToFeature,
      mouseout: resetHighlight,
    });
  }

  //add these events to the layer object
  function onEachFeature1(feature, layer) {
    layer.on({
      mouseover: highlightFeature1,
      click: zoomToFeature1,
      mouseout: resetHighlight1,
    });
  }

  function getColor(d) {
    return d > 70
      ? "#2c7bb6"
      : d >= 60
      ? "#abd9e9"
      : d >= 50
      ? "#fdae61"
      : d < 50
      ? "#d7191c"
      : "#5f5f5f";
  }

  function getColor1(d) {
    return d >= 75
      ? "#1a9641"
      : d >= 50
      ? "#a6d96a"
      : d >= 25
      ? "#fdae61"
      : d > 0
      ? "#d7191c"
      : "#5f5f5f";
  }

  function states(feature) {
    return {
      weight: 3,
      opacity: 1,
      color: "whitesmoke",
      fillOpacity: 0.8,
      fillColor: getColor(feature.properties.vaxData_states_Series_Complete),
    };
  }

  function counties(feature) {
    return {
      weight: 0.5,
      opacity: 0.5,
      color: "whitesmoke",
      fillOpacity: 0.75,
      fillColor: getColor1(feature.properties.vaxData_Series_Complete),
    };
  }

  // add state borders
  var geojsonStates = L.geoJson.ajax("data/us_states.geojson", {
    style: states,
    onEachFeature: onEachFeature,
  });

  geojsonStates.addTo(map);

  //add county geojson
  var geojsonCounties = L.geoJson.ajax("data/counties.geojson", {
    style: counties,
    onEachFeature: onEachFeature1,
  });

  geojsonCounties.addTo(map);

Best Answer

You need to store the current zoom level when zoomed to a state using stateZoomLevel = map.getBoundsZoom(e.target.getBounds());. Then you need an event listener on zoomend and check if the stored zoom level is bigger than the current one. If so you need to hide the counties and show the states. Here is a sample using your code. I have used fetch to query the data and the leaflet-layervisibility plugin for toggeling the layers.

<!DOCTYPE html>
<html>

<head>

  <title>make-data-layer-disappear-and-different-one-appear-on-click-in-leaflet</title>

  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0">

  <!-- Leaflet -->
  <link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/leaflet.css"
    integrity="sha512-xodZBNTC5n17Xt2atTPuE1HxjVMSvLVW9ocqUKLsCC5CXdbqCmblAshOMAS6/keqq/sMZMZ19scR4PsZChSR7A=="
    crossorigin="" />
  <script src="https://unpkg.com/[email protected]/dist/leaflet.js"
    integrity="sha512-XQoYMqMTK8LvdxXYG3nZ448hOEQiglfqkJs1NOQV44cWnUrBc8PkAOcXy20w0vlaXaVUearIOBhiXZ5V3ynxwA=="
    crossorigin=""></script>

  <!-- Leaflet Visibility -->
  <script src="https://unpkg.com/leaflet-layervisibility/dist/leaflet-layervisibility.js"></script>


  <style>
    body {
      padding: 0;
      margin: 0;
    }

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

<body>

  <div id="map"></div>
  <script>
    var map = L.map("map", {
      center: [39.5, -95.3],
      zoom: 3.5,
    });

    (async () => {
      let stateZoomLevel;

      // add basemap
      var Stamen_TonerLite = L.tileLayer(
        "https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png", {
          attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors | &copy; <a href="https://carto.com/attributions">CARTO</a> | <a href="https://data.cdc.gov/Vaccinations/COVID-19-Vaccinations-in-the-United-States-County/8xkx-amqh/data">County data</a> | <a href="https://data.cdc.gov/Vaccinations/COVID-19-Vaccinations-in-the-United-States-Jurisdi/unsk-b7fc/data">State data</a> | Map: <a href="https://weircf.wixsite.com/e-portfolio">Chip Weir</a>',
          subdomains: "abcd",
          maxZoom: 19,
        }
      ).addTo(map);

      //zoom to a state, turn off state data, turn on county data
      function zoomToFeature(e) {
        map.fitBounds(e.target.getBounds());
        showCountiesHideStates(e.target.feature.properties.STATE);

        // Save current Zoom Level
        stateZoomLevel = map.getBoundsZoom(e.target.getBounds());
      }

      //zoom to county
      function zoomToFeature1(e) {
        map.fitBounds(e.target.getBounds());
      }

      // Event Listener
      map.on("zoomend", (x) => {
        if (stateZoomLevel > map.getZoom()) {
          showStatesHideCounties();
        }
      })

      function highlightFeature(e) {
        var layer = e.target;
        layer.setStyle({
          weight: 4,
          opacity: 1,
          color: "#dbff4d",
        });
        layer.bringToFront();
      }

      function highlightFeature1(e) {
        var layer = e.target;
        layer.setStyle({
          weight: 4,
          opacity: 1,
          color: "#dbff4d",
        });
        layer.bringToFront();
      }

      //reset the hightlighted states on mouseout
      function resetHighlight(e) {
        geojsonStates.resetStyle(e.target);
      }

      //reset the hightlighted counties on mouseout
      function resetHighlight1(e) {
        geojsonCounties.resetStyle(e.target);
      }

      //add these events to the layer object
      function onEachFeature(feature, layer) {
        layer.on({
          mouseover: highlightFeature,
          click: zoomToFeature,
          mouseout: resetHighlight,
        });
      }

      //add these events to the layer object
      function onEachFeature1(feature, layer) {
        layer.on({
          mouseover: highlightFeature1,
          click: zoomToFeature1,
          mouseout: resetHighlight1,
        });
      }

      function getColor(d) {
        return d > 70 ?
          "#2c7bb6" :
          d >= 60 ?
          "#abd9e9" :
          d >= 50 ?
          "#fdae61" :
          d < 50 ?
          "#d7191c" :
          "#5f5f5f";
      }

      function getColor1(d) {
        return d >= 75 ?
          "#1a9641" :
          d >= 50 ?
          "#a6d96a" :
          d >= 25 ?
          "#fdae61" :
          d > 0 ?
          "#d7191c" :
          "#5f5f5f";
      }

      function states(feature) {
        return {
          weight: 3,
          opacity: 1,
          color: "whitesmoke",
          fillOpacity: 0.8,
          fillColor: getColor(feature.properties.vaxData_states_Series_Complete),
        };
      }

      function counties(feature) {
        return {
          weight: 0.5,
          opacity: 0.5,
          color: "whitesmoke",
          fillOpacity: 0.75,
          fillColor: getColor1(feature.properties.vaxData_Series_Complete),
        };
      }

      // add state borders
      const geojsonStatesData = await fetch("data/us_states.geojson").then(r => r.json());
      var geojsonStates = L.geoJson(geojsonStatesData, {
        style: states,
        onEachFeature: onEachFeature,
      });

      geojsonStates.addTo(map);

      //add county geojson
      const geojsonCountiesData = await fetch("data/counties.geojson").then(r => r.json());
      var geojsonCounties = L.geoJson(geojsonCountiesData, {
        style: counties,
        onEachFeature: onEachFeature1,
      });

      geojsonCounties.addTo(map);

      function showStatesHideCounties() {
        geojsonStates.show();
        geojsonCounties.hide();
      }

      function showCountiesHideStates(stateId) {
        if (stateId) {
          geojsonCounties.show((layer) => {
            return layer.feature.properties.STATE === stateId;
          });
        } else {
          geojsonCounties.show();
        }
        geojsonStates.hide();
      }

      // Init
      showStatesHideCounties();
    })();
  </script>

</body>

</html>