Leaflet map markers clearLayers() before new position data

Viewed 2477

I am working on a Leaflet map with markers.I am getting marker latlong data from a file that is updated every 5 seconds with new position data for about 100 markers.

I then use switch to break the markers into 2 groups which are represented on the map with different icons.

My problem is that each time new position data is updated new markers are created but the old markers remain, so before long there is a trail of markers and the map becomes very slow.

I want to clear the old marker each time new markers are created and have tried using the clearLayers() function to no effect. There are no errors, but the markers remain. I have moved the clearLayers() to other levels in the code to either no effect or completely removing all markers including any new markers.

geojson.js

     async function getData() {

        const response = await fetch('../../Export.log');
        var data = await response.text();
        var formatted = JSON.parse('[' + data.trim().replace(/\n/g, ',') + ']')
        var enemyForces = L.layerGroup().addTo(map);
        var alliedForces = L.layerGroup().addTo(map);
        alliedForces.clearLayers();
        enemyForces.clearLayers();

        for (var i = 0; i < formatted.length; i++){
            var unit = formatted[i];
            var coalition = unit.coalition;
            var Lat = unit.Lat;
            var Long = unit.Long;
            var LatLong = [Lat, Long];
            var LongLat = [Long, Lat];
            var name = unit.name;
            var ID = unit.ID;
        

            switch (coalition) {
                case "Enemies":

                    L.marker([Lat, Long], {icon: enemyIcon}).bindPopup(name + "<br>" + coalition + "<br>" + ID).addTo(enemyForces);

                    break;
                case "Allies":

                    L.marker([Lat, Long], {icon: alliedIcon}).bindPopup(name + "<br>" + coalition + "<br>" + ID).addTo(alliedForces);

            } // End switch
        
        } // End for

    }
        
        getData();
        setInterval(getData, 5000);

index.html

<!DOCTYPE html>
<html>
<head>
    
    <title>Test Map</title>

    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    
    <link rel="shortcut icon" type="image/x-icon" href="/favicon.ico" />

    <link rel="stylesheet" href="https://unpkg.com/leaflet@1.6.0/dist/leaflet.css" integrity="sha512-xwE/Az9zrjBIphAcBb3F6JVqxf46+CDLwfLMHloNu6KEQCAWi6HcDUbeOfBIptF7tcCzusKFjFw2yuvEpDL9wQ==" crossorigin=""/>
    <script src="https://unpkg.com/leaflet@1.6.0/dist/leaflet.js" integrity="sha512-gZwIG9x3wUXg2hdXF6+rVkLF/0Vi9U8D2Ntg4Ga5I5BZpVkVxlJWbSQtXPSiUTtC0TjtGOmxa1AJPuV0CPthew==" crossorigin=""></script>

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

  </head>
  <body>
    <div id='map'></div>
<script src="js/geojson.js" type="text/javascript"></script>
<script src="js/map.js" type="text/javascript"></script>
  </body>
</html>

map.js

// Start Map
var map = L.map('map').setView([33.5, -117.0], 7);

L.tileLayer('https://api.mapbox.com/styles/v1/{id}/tiles/{z}/{x}/{y}?access_token=pk.eyJ1IjoibWFwYm94IiwiYSI6ImNpejY4NXVycTA2emYycXBndHRqcmZ3N3gifQ.rJcFIG214AriISLbB6B5aw', {
    maxZoom: 18,
    attribution: 'Map data &copy; <a href="https://www.openstreetmap.org/">OpenStreetMap</a> contributors, ' +
        '<a href="https://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>, ' +
        'Imagery © <a href="https://www.mapbox.com/">Mapbox</a>',
    id: 'mapbox/light-v9',
    tileSize: 512,
    zoomOffset: -1
}).addTo(map);

var unitIcon = L.Icon.extend({
    options: {
        iconSize:     [32, 37],
        iconAnchor:   [0, 0],
        popupAnchor:  [0, -5]
    }
});

var alliedIcon = new unitIcon({iconUrl: 'img/ally.png'}),
    enemyIcon = new unitIcon({iconUrl: 'img/enemy.png'});


function onEachFeature(feature, layer) {
// does this feature have a property named popupContent?
if (feature.properties && feature.properties.popupContent) {
    layer.bindPopup(feature.properties.popupContent);
}
}

Export.log

{"ID" : 50, "name" : "Blue1", "coalition" : "Allies", "Lat" : 33.331, "Long" : -117.162, "Alt" : 10.160897, "heading" : 6.283185}
{"ID" : 51, "name" : "Red1", "coalition" : "Enemies", "Lat" : 33.374, "Long" : -116.835, "Alt" : 0.045466, "heading" : 4.879922}
{"ID" : 52, "name" : "Blue2", "coalition" : "Allies", "Lat" : 33.783, "Long" : -117.228, "Alt" : 6.703342, "heading" : 3.078192}
2 Answers

Every time you call getData, you're creating a brand new enemyForces and alliedForces. You're adding it to the map. When you run the switch statement, if modifies the enemyForces and alliedForces of the current function closure, but does not affect those variables from previous function closures. Move those variable declarations outside the function.


// move these definitions outside of your function
var enemyForces = L.layerGroup().addTo(map);
var alliedForces = L.layerGroup().addTo(map);

async function getData(){

  ...

  enemyForces.clearLayers();
  alliedForces.clearLayers();

  for (...){

    ...

    switch (coalition) {

      case "Enemies":
        L.marker([Lat, Long], {icon: enemyIcon}).bindPopup(...).addTo(enemyForces);
        break;

      case "Allies":
        L.marker([Lat, Long], {icon: alliedIcon}).bindPopup(...).addTo(alliedForces);

    } // end switch
  } // end for
 
}

Edit:

Changed answer. Using clearLayer should be sufficient without needing to call remove()

You can provide as follows ;

clearLayersByType(type) {
  if(type == 'enemyForces') {
    this.enemyForces ? this.enemyForces.clearLayers() : null ;
  }
}
Related