最近使用 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()
或类似的吗?
在每次移动时,只要它们在边界内,您就会一次又一次地添加相同的标记。在某些时候,地图上会有太多标记,浏览器会崩溃。
您需要执行以下操作:
此代码未经测试!我在我的项目中使用了不同的版本,这个版本有效。我还认为你需要用像 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();
}
您可能还想限制后端每个响应的条目数。
我们可以测试标记是否在
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)