Epidemiology & Technology

Removing and Updating GEOJSON Layers in Leaflet Maps, Using LayerGroups in Leaflet

Use Case:

  • To show state boundaries in country map.
  • When a state is selected, show district boundries within that state.
  • If a new state is selected,
    • Remove district boundries from previous state
    • Show district boundaries in newly selected state

Let us map it

Directrory structure

..
Index.html
 geo
      |--india.geojson
      |-- state1.geojson
      |--state2.geojson

The html has a state selection dropdown

<!DOCTYPE html>
<html> <head>
  <link rel="stylesheet" href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css"
  integrity="sha512-xodZBNTC5n17Xt2atTPuE1HxjVMSvLVW9ocqUKLsCC5CXdbqCmblAshOMAS6/keqq/sMZMZ19scR4PsZChSR7A=="
  crossorigin=""/>
<script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js"
  integrity="sha512-XQoYMqMTK8LvdxXYG3nZ448hOEQiglfqkJs1NOQV44cWnUrBc8PkAOcXy20w0vlaXaVUearIOBhiXZ5V3ynxwA=="
  crossorigin=""></script>
<script src = "https://cdnjs.cloudflare.com/ajax/libs/leaflet-ajax/2.1.0/leaflet.ajax.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
</head>
<body>
  <div id="statesFilter"> 
    <label>  <span>State: </span>   <br>  
      <span>    
        <select name="states" id="states"> 
                <option value=""></option>      
                <option value="35">ANDAMAN &amp; NICOBAR ISLANDS</option>
                <option value="28">ANDHRA PRADESH</option>
                <option value="12">ARUNACHAL PRADESH</option>
                <option value="18">ASSAM</option>
                <option value="10">BIHAR</option>
                <option value="4">CHANDIGARH</option>
                <option value="22">CHATTISGARH</option>
                <option value="26">DADRA &amp; NAGAR HAVELI</option>
                <option value="25">DAMAN &amp; DIU</option>
                <option value="7">DELHI</option>
                <option value="30">GOA</option>
                <option value="24">GUJARAT</option>
                <option value="6">HARYANA</option>
                <option value="2">HIMACHAL PRADESH</option>
                <option value="1">JAMMU &amp; KASHMIR</option>
                <option value="20">JHARKHAND</option>
                <option value="29">KARNATAKA</option>
                <option value="32">KERALA</option>
                <option value="37">LADAKH</option>
                <option value="31">LAKSHADWEEP</option>
                <option value="23">MADHYA PRADESH</option>
                <option value="27">MAHARASHTRA</option>
                <option value="14">MANIPUR</option>
                <option value="17">MEGHALAYA</option>
                <option value="15">MIZORAM</option>
                <option value="13">NAGALAND</option>
                <option value="21">ODISHA</option>
                <option value="34">PUDUCHERRY</option>
                <option value="3">PUNJAB</option>
                <option value="8">RAJASTHAN</option>
                <option value="11">SIKKIM</option>
                <option value="33">TAMIL NADU</option>
                <option value="36">TELANGANA</option>
                <option value="16">TRIPURA</option>
                <option value="9">UTTAR PRADESH</option>
                <option value="5">UTTARAKHAND</option>
                <option value="19">WEST BENGAL</option>
            </select>  
        </span>  
    </label>
 </div>

  <div class="mapArea" id="map" style="height: 500px;"> </div>
</body>

<script>

// Initialize LEAFLET MAP
var map= L.map('map'); 
// Basetileyyer
function addTileLayer() {
    tilelayer =   L.tileLayer('https://{s}.basemaps.cartocdn.com/light_nolabels/{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>',
    subdomains: 'abcd' });
  tilelayer.addTo(map); 
}


// India Map  = iMap
// VISIBLE ALL TIMES
let iMapData;
function buildIndiaMap2(unit) {
 iMapData = new L.GeoJSON.AJAX("../geo/"+ unit +".geojson");
 iMapData.on('data:loaded', function() { 
    iMapData.addTo(map);   
    map.fitBounds(iMapData.getBounds()); 
})
}Code language: HTML, XML (xml)

The state geoJSONs are saved as 1.geojson, 2.geojson etc in the geo directory.

We attach an event listner to the Dropdown Select control and show state layers absed on selection

/// State Map VISIBLE ONLY WHEN STATE SELECTED
var sLayerGroup = new L.LayerGroup();
sLayerGroup.addTo(map);
function makeStateMap(state)  {
  var sMap = new L.GeoJSON.AJAX("/geo/"+ state + ".geojson");
  sMap.on('data:loaded', function() { 
    sLayerGroup.clearLayers();
    sLayerGroup.addLayer(sMap); 
    map.fitBounds(sMap.getBounds()); 
  }) 
}

function stateListener() {
  document.getElementById("statesFilter")
  .addEventListener('change', (event) => {
    selectedState = event.target.value;
    if (selectedState =="")   {
      sLayerGroup.clearLayers(); 
      map.fitBounds(iMapData.getBounds());}
    else  {
      makeStateMap(selectedState); 
    }
  });
}

// WHEN Document is  Ready  --> Create Map 
$(document).ready(function(){  
  addTileLayer();
  buildIndiaMap2('India');
  stateListener();
});

Code language: JavaScript (javascript)

So what are these layergroups?

These are just groups of layers in leaflet. When it comes to GeoJSON, each feature is a layer and loading a geojson file is akin to making a new layergroup. But you do not know that name of that layergroup and therefore cannot remove its layers / features.

To solve this issue,

  1. We create first a new L.LayerGroup (called sLayerGroup here) – var sLayerGroup = new L.LayerGroup();
  2. Add the LAYERGROUP to the map – sLayerGroup.addTo(map);
  3. At this point, there are no layers / features in the layergroup so nothing is visibly chnaged on map
  4. We load the GeoJSON using AJAX : var sMap = new L.GeoJSON.AJAX("/geo/"+ state + ".geojson"); This creates the Leafelt GeoJSON Layer Object- sMap.
  5. Then we clear all layers from the layergroup – nothing exists so nothing to clear for now. sLayerGroup.clearLayers();
  6. Then we add the GeoJSON layer object to the LayerGroup. sLayerGroup.addLayer(sMap). THIS STEP IS IMPORTANT. Conventionaly, we would have added the GeoJson layer object just created to the map but here we add to layergroup.
  7. Since the LayerGroup has already been added to the map, adding new layer to the layergroup makes it visible on the map
  8. Now we zoom to the newly created layer by: map.fitBounds(sMap.getBounds());

If selection of state changes, the stateListener event listener gets activated. In case the state selection has a valid value, the makeStateMap function is called again. At step 3, now the previous states features will be cleared out and new selected state’s geojson features are added to the LayerGroup. Since the Layergroup is always added to map, the new features are now visible. We also zoom to the new layer since fitBounds method gets called as well.

If no state is selected, we clearLayers of sLayerGroup, and zoom out to India, by fitting map bounds to iMapData. In order for zoom put to work, we have made iMapData variable a global variable by declaring it outside of buildIndiaMap2 function.

Full code here: LINK

BONUS: the geo folder has Indian state wise GeoJSON files with District polygons.

Related posts