如何让Leaflet在其bbox之外删除或停止处理标记?

问题描述 投票:0回答:3

最近使用 GeoDjango /rest_framework_gis 设置了 Leaflet 地图,并且使用 Pagination 得到了有限的结果,但结果似乎仍然是 Leaflet 累积地处理它收到的每个标记,而不仅仅是视图中的内容,这会导致浏览器滞后并最终崩溃。在评论中,我被建议用 JS 来解决这个问题。这是有道理的,因为它是前端,但我们怎样才能做到这一点?

来自本教程的 JS:

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

我们可以编写某种函数来执行某些操作,例如如果有超过 n 个条目从 bbox 视图中删除最远的条目吗?

具体线路好像是

.addTo(map);
。 JS 中有某种
.removeFrom()
或类似的吗?

javascript django-rest-framework leaflet geodjango
3个回答
2
投票

在每次移动时,只要它们在边界内,您就会一次又一次地添加相同的标记。在某些时候,地图上会有太多标记,浏览器会崩溃。

您需要执行以下操作:

  1. 添加检查缩放是否足够高。不要加载低于 12 的条目。否则请求可能会太大。但这是可选的
  2. 仅加载新标记(如果它们尚未位于加载的范围内)。
  3. 停止/中止所有正在运行的请求
  4. 创建一个请求并将范围扩大 10%,以便不要每次移动都请求
  5. 请求完成后:将所有现有图层复制到数组中,并从数组中删除请求结果中的图层。数组的最后将是需要删除的所有层(因为我们过滤掉了所需的一层)。请求中不存在于现有图层数组中的图层是新图层,需要添加到地图中。

此代码未经测试!我在我的项目中使用了不同的版本,这个版本有效。我还认为你需要用像 fetch 这样的普通请求替换 ajax / jquery 请求

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);
    });

}

1
投票

此后,我通过更好地了解我的需要解决了这个问题。通常最好在通过添加

clearMarkers()
或类似内容添加新响应中的标记之前清除所有标记:

async function load_markers() {
const markers_url = `/api/markers/?in_bbox=${map.getBounds().toBBoxString()}`
const response = await fetch(markers_url)
const geojson = await response.json()
clearMarkers();
return geojson
}

function clearMarkers() {
  layerGroup.clearLayers();
}

您可能还想限制后端每个响应的条目数。


0
投票

我们可以测试标记是否在

bbox
中并进行相应操作。

const inBound = (coord, bbox) => (coord[0] > bbox[0] && coord[0] < bbox[2] && coord[1] > bbox[1] && coord[1] < bbox[3]) ? true : false
const bounds = map.getBounds()
const bbox = [bounds._southWest.lat.toFixed(3), bounds._southWest.lng.toFixed(3) , bounds._northEast.lat.toFixed(3) , bounds._northEast.lng.toFixed(3)]

inBound([50.079, 14.418], bbox)
© www.soinside.com 2019 - 2024. All rights reserved.