如何使 Leaflet 丢弃或停止处理其 bbox 之外的标记?

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

最近 and have ,但结果似乎仍然让 Leaflet 累积处理它收到的每个标记,而不仅仅是视图中的内容,这会滞后并最终导致浏览器崩溃。在评论中我被建议用 JS 来解决这个问题。这是有道理的,因为它是前端,但我们如何才能做到这一点?

来自 this tutorial 的 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()或类似的东西?

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

您需要执行以下操作:

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

}

我已经通过更好地了解我的需要解决了这个问题。通常最好在通过添加 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();
}

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