刷新leaflet地图:地图容器已经初始化。

99

我有一个页面,在给用户提供选择器后,他可以切换我展示的leaflet地图。

在最初加载leaflet地图之后,我的问题是当我想要刷新地图时。

我总是会得到“Map container is already initialized”:

问题出在这一行:

var map = L.map('mapa').setView([lat, lon], 15);

一开始加载得很好,但当我在表单中选择另一个参数并想再次显示地图时,它就会崩溃。

顺便说一句,我尝试在第二次setView()之前使用jQuery销毁和重新创建$('#mapa'),但仍然显示相同的错误。


我在使用Jest进行单元测试时,曾经遇到过类似于nuxt-leaflet的错误,但现在无法再次复现。根据测试,我可以通过创建一个虚拟div来解决它。const dummyDiv: HTMLDivElement = global.document.createElement("div"); const dummyID: string = "dummyID"; dummyDiv.setAttribute("id", dummyID); global.document.body.appendChild(dummyDiv);`然后在shallowMount中将其附加:wrapper = shallowMount(MapLeaflet, { attachTo: dummyDiv, localVue, store, stubs: { NuxtLink: RouterLinkStub, }, }); - Daniel Danielecki
26个回答

127

在尝试重新加载地图之前,尝试使用 map.remove();。这将使用 Leaflet 库(而不是 jQuery 库)删除先前的地图元素。


40
如果(map != undefined) { map.remove(); } 如果你不确定地图是否存在... - Johan Hoeksma
我不得不这样使用 map = map.remove() 才能使其正常工作,因为我注意到 remove() 的返回类型也是一个 Map 对象。 - OCP30pt1c1l1l43-X1z17

55

最好的方法

map.off();
map.remove();

你应该添加 map.off(),它的效率更高,并且不会导致事件问题。


5
对不起,您如何使用这个? - djack109
@djack109 保留地图处理程序对象的引用。您可以从地图的onReady事件中获取它。然后,您可以在该处理程序对象上调用这两种方法。如果不清楚,我很乐意提供一个示例。 - Jack
请确保map是全局变量,这样您就可以检查地图是否已经设置。使用truthy:if(map) { 然后,设置 map = map.off();map = map.remove(); 采用这种方法,无需重置div容器的innerHTML。 - like2think

43

Html:

<div id="weathermap"></div>

JavaScript:

function buildMap(lat,lon)  {
    document.getElementById('weathermap').innerHTML = "<div id='map' style='width: 100%; height: 100%;'></div>";
    var osmUrl = 'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
                    osmAttribution = 'Map data © <a href="http://openstreetmap.org">OpenStreetMap</a> contributors,' +
                        ' <a href="http://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>',
    osmLayer = new L.TileLayer(osmUrl, {maxZoom: 18, attribution: osmAttribution});
    var map = new L.Map('map');
    map.setView(new L.LatLng(lat,lon), 9 );
    map.addLayer(osmLayer);
    var validatorsLayer = new OsmJs.Weather.LeafletLayer({lang: 'en'});
    map.addLayer(validatorsLayer);
}

我使用这个:

document.getElementById('weathermap').innerHTML = "<div id='map' style='width: 100%; height: 100%;'></div>";
重新加载渲染地图的div内容。

重新加载渲染地图的div内容。


在普通的HTML中可能有效,但在像React这样的库中,由于选择性div操作,它可能无效。 - vibhor vaish

25

在初始化地图之前,请检查地图是否已经初始化。

var container = L.DomUtil.get('map');
      if(container != null){
        container._leaflet_id = null;
      }

这适用于 react-leaflet 版本 4.2.1 和 nextjs 13.4.12。在具有地图容器的函数中使用 useEffect。 - MD. MEHEDI IMAM

13

这是唯一对我有效的解决方案。 - Grzes Slania

6

你应该尝试在React JS中卸载函数以删除现有地图。

const Map = () => {

    const mapContainer = useRef();
    const [map, setMap] = useState({});

    useEffect(()=>{
        const map = L.map(mapContainer.current, {attributionControl: false}).setView([51.505, -0.09], 13);

    L.tileLayer('https://api.mapbox.com/styles/v1/{id}/tiles/{z}/{x}/{y}?access_token=pk.eyJ1IjoibWFwYm94IiwiYSI6ImNpejY4NXVycTA2emYycXBndHRqcmZ3N3gifQ.rJcFIG214AriISLbB6B5aw', {
        maxZoom: 18,
        attribution: 'Map',
        id: 'mapbox/streets-v11',
        tileSize: 512,
        zoomOffset: -1
    }).addTo(map);

    // unmount map function
    return () => map.remove();
    }, []);

    return (
        <div style={{padding: 0, margin: 0, width: "100%", height: "100vh",}}
             ref={el => mapContainer.current = el}>
        </div>
    );
}

尤其是在开发模式下运行时,useEffect函数会被调用两次,这就导致了问题的出现。通过使用清理函数可以解决这个问题。如果使用TypeScript,可以将map.remove()放在{}中。 - nurp

5
当您只删除地图时,它会破坏div id的引用,因此在remove()之后,需要重新构建包含地图的div,以避免出现“未捕获错误:找不到地图容器”。
if(map != undefined || map != null){
    map.remove();
   $("#map").html("");
   $("#preMap").empty();
   $( "<div id=\"map\" style=\"height: 500px;\"></div>" ).appendTo("#preMap");
}

5

经过不断搜索,我发现这个问题在 http://leafletjs.com/examples/layers-control.html 中有详细的文档说明。

我最终没有重新绘制地图,而是在每次新的ajax调用时打印并重新绘制点,所以问题是如何清除旧的点并只打印新的点。我最终做到了以下几点:

var point = L.marker([new_marker[0], new_marker[1]]).addTo(map).bindPopup('blah blah');
points.push(point); 
//points is a temporary array where i store the points for removing them afterwards

因此,在每个新的ajax调用中,在绘制新点之前,我会执行以下操作:

for (i=0;i<points.length;i++) {
  map.removeLayer(points[i]);
}
points=[];

目前为止,一切都很不错 :-)


4
您可以尝试在初始化地图之前或离开页面时删除地图:
if(this.map) {
  this.map.remove();
}

3

在Angular中,当切换页面时我遇到了相同的问题。我必须在离开页面之前添加以下代码以使其工作:

    $scope.$on('$locationChangeStart', function( event ) {
    if(map != undefined)
    {
      map.remove();
      map = undefined
      document.getElementById('mapLayer').innerHTML = "";
    }
});

没有 document.getElementById('mapLayer').innerHTML = "",地图在下一页上无法显示。

这对我很有帮助。我正在使用Angular 6,并根据用户点击的位置更改地图。 我只是有一个方法来创建一个新的地图对象。当我更新地图时,我将现有的地图对象传递进去,并且不需要innerHTML部分就可以完成上述操作... - Kevin van Zyl
这个方法是可行的,但是我并不真正理解 map.remove()map = undefined 语句的区别。 - Sapinder Singh
这段代码应该放在哪里? 我有一个组件,其中有 <div id="mapDetails"></div> 并且在.ts文件中我像这样初始化了地图:`this.map = L.map('mapDetails', { center: [40.4379543,-3.6795367], zoom: 6 }); const tiles = L.tileLayer( 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { maxZoom: 19, minZoom: 6, attribution: '© <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>' } ); tiles.addTo(this.map);` - Emili Bellot Pulido

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接