如何使 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()
或类似的东西?
在每次移动中,只要它们在边界内,您就会一次又一次地添加相同的标记。有时地图上的标记过多,浏览器会崩溃。
您需要执行以下操作:
- 添加检查缩放是否足够高。不要加载 12 以下的条目。否则请求可能会很大。但这是可选的
- 如果新标记不在加载范围内,则只加载它们。
- 停止/中止所有 运行 请求
- 创建一个请求并扩展 10% 的范围,而不是每次移动都请求
- 完成请求后:将所有现有图层复制到一个数组中,并从数组中删除请求结果中的图层。在数组的末尾将是需要删除的所有层(因为我们过滤掉了需要的层)。请求中不在现有图层数组中的图层是新图层,需要添加到地图中。
此代码未经测试!我在我的项目中使用了不同的版本,这个版本有效。我还认为你需要用像 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();
}
您可能还想在后端限制每个响应的条目数。
最近
来自 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()
或类似的东西?
在每次移动中,只要它们在边界内,您就会一次又一次地添加相同的标记。有时地图上的标记过多,浏览器会崩溃。
您需要执行以下操作:
- 添加检查缩放是否足够高。不要加载 12 以下的条目。否则请求可能会很大。但这是可选的
- 如果新标记不在加载范围内,则只加载它们。
- 停止/中止所有 运行 请求
- 创建一个请求并扩展 10% 的范围,而不是每次移动都请求
- 完成请求后:将所有现有图层复制到一个数组中,并从数组中删除请求结果中的图层。在数组的末尾将是需要删除的所有层(因为我们过滤掉了需要的层)。请求中不在现有图层数组中的图层是新图层,需要添加到地图中。
此代码未经测试!我在我的项目中使用了不同的版本,这个版本有效。我还认为你需要用像 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();
}
您可能还想在后端限制每个响应的条目数。