Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make Leaflet drop or stop processing markers outside of its bbox?

Recently set up Leaflet map with GeoDjango / rest_framework_gis and have limited results with Pagination, but the result still seems to have Leaflet cumulatively processing every marker it receives, not just what's in view, which lags and eventually crashes browsers. In the comment I was advised to address this with JS. This makes sense as it's front end, but how can we do this?

The JS from this tutorial:

async function render_markers() {
const markers = await load_markers();
L.geoJSON(markers)
    .bindPopup((layer) => layer.feature.properties.name)
    .addTo(map);
}

Can we write some kind of function that'll do something along the lines of if there are over n entries drop the furthest from bbox view?

The specific line seems to be .addTo(map);. Is there some kind of .removeFrom() or similar in JS?

like image 439
Chris Dixon Avatar asked Oct 14 '25 15:10

Chris Dixon


1 Answers

on every move you will add same markers again and again as long as they are in the bounds. At some point there will be to much markers on the map and the browser will crash.

You need to do following:

  1. Add a check if the zoom is high enough. Don't load the entries below 12. Else the request will maybe to big. But this is optional
  2. load only new markers if they are not already in the loaded bounds.
  3. Stop / abort all running requests
  4. Create an Request and extend the bounds with 10% to request not on every move
  5. After finishing the request: copy all exisiting layers to an array and remove the layers from the array which are in the request result. At the end in the array will be all layers they need to be removed (because we filtered the needed one out). The layers from the request which are not in the existing layer array, are new and needed to be added to the map.

This code is not tested! I use a different version in my project and this one works. also I think you need to replace the ajax / jquery request with vanilla requests like fetch

var runningRequests = [];
var allLayers = [];

function initLoad() {
    // add event listeners to load the new layers
    map.on('moveend',mapMove);
    map.on('zoomend',mapMove);

    // load initial the layers
    if (map.getZoom() > 12) {
        loadLayers(map);
    }
}

function mapMove() {
    // only load new layers if higher then zoom level 12
    if (map.getZoom() > 12) {
        // only load new layers if the map bounds are not in the loaded bounds anymore
        if (areaBounds.contains(map.getBounds()) === false) {
            loadLayers()
        }
    }
}

function loadLayers() {
    // current bounds extending by 15%
    areaBounds = map.getBounds().pad(0.15);

    // abort all running requests
    if(runningRequests.length > 0){
        for (var i in runningRequests) {
            runningRequests[i].abort(); // jquery request abort -> not working for vanilla requests
            delete runningRequests[i];
        }
    }


    var req = $.ajax(options).done(function(json){  // jquery load request -> not working for vanilla requests
        var layersToRemove = allLayers; // maybe this doesn't break the reference then you need to destroy them
        
        len = json.length;
        
        var layersToAdd = [];

        json.forEach((obj)=> {
            var coords = obj.coordinates;
            // filter all layers out, which has the same coordinates as in the json
            var filteredLayers = layersToRemove.filter(function (layer) { return !layer.getLatLng().equals([coords[1], coords[0]]) });
            
            if(filteredLayers.length === layersToRemove.length){
                // no layer was removed, so we know that it is a new layer
                layersToAdd.push(obj);
            }
            
            layersToRemove = filteredLayers;
        });
        
        // remove all layers that are not in the result anymore
        layersToRemove.forEach((layer)=>{;
            map.removeLayer(layer);
        }
        
        addLayer(layersToAdd);

    });

    runningRequests.push(req);
}

function addLayer(geoJsonLayers){
    
    // the data are in geojson format, so we add them to a featureCollection, so we can load it with L.geoJSON(featureCollection)
    var featureCollection = {
      "type": "FeatureCollection",
      "features": []
    };
    featureCollection.features = geoJsonLayers;
    
    // add layers to the map
    var geoJson = L.geoJSON(featureCollection)
        .bindPopup((layer) => layer.feature.properties.name)
        .addTo(map);
    
    // add the new layers to the allLayers array
    geoJson.getLayers().forEach((layer)=>{
        allLayers.push(layer);
    });

}
like image 194
Falke Design Avatar answered Oct 17 '25 05:10

Falke Design



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!