You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Leaflet重建地图后WMS图层不显示问题求助

Leaflet WMS图层切换后无法显示的问题修复

问题描述

我开发了一个Flask Python应用,流程是:用户在页面选择车辆,系统通过Ajax从数据库获取该车辆对应的WMS图层配置,在Leaflet的图层控件中提供对应的图层供勾选开关。所有船舶标记被整合到同一个图层组,减少复选框数量。

问题现象:首次选择车辆时功能完全正常,但切换到其他车辆后再切回原车辆,WMS图层在图层控件中显示为选中状态,但实际不会在地图上渲染出来。

原JavaScript代码

var vesselVar = null;
var mapVessel = null;
var layerControl = null;
var baseMaps = null;
var overlayMaps = null;
var layerNameArr = null;
var layerLinkArr = null;
var baseMapArr = null;
var myVessels = null;
var sLayerName = null;
var sUrl = null;
var wmsOptions = null;
var myWmsLayer = null;

function myOnSelectedVessel() {
    let sSelectedVessel = $('#sSelectedVessel').find(":selected").text()
    if (sSelectedVessel.length > 0) {
        $.ajax({
            url: '/vessel_map_set_vessel_id',
            data: {
                'sSelectedVesselName': sSelectedVessel,
            },
            type: 'POST',
            dataType: 'json',
            success: function (response) {
                if (response.result) {
                    alert(response.msg);
                }
                myCreateOverlay(response.liVessels, response.liLastVesselPos, response.arrLeaflet.toString());
                myCreateBaseLayers();
                myCreateMap(response.lat, response.lon);
                mapVessel.panTo(new L.LatLng(parseFloat(response.lat), parseFloat(response.lon)));
            },
            error: function (error) {
                alert("error in function vessel_map.myOnSelectedVessel");
            }
        });
    }
}


function myCreateVesselGroup(liVessels, liLastVesselPos) {
    try {
        myVessels = null;
        vesselVar = null;
        myVessels = L.layerGroup([]);
        vesselVar = [];
        let iLen = liLastVesselPos.length;
        const arrVesselNames = liVessels.toString().split(',');
        const arrVesselPos = liLastVesselPos.toString().split(',');
        let iCntVesselPos = 0;
        for (let iCn7 = 0; iCn7 < iLen; iCn7++) {
            arrPos = liLastVesselPos[iCn7];
            myAddVesselMarker(arrVesselNames[iCn7], arrVesselPos[iCntVesselPos], arrVesselPos[iCntVesselPos + 1]);
            myVessels.addLayer(vesselVar[iCn7]);
            iCntVesselPos += 2;
        }
    }
    catch (err) {
        alert("Error in function myCreateBaseLayers: " + err.message);
    }
}


function myCreateOverlay(liVessels, liLastVesselPos, sLeafletWmsString) {
    try {
        if (mapVessel !== null) {

            if ((overlayMaps !== null) && (layerControl !== null)) {
                mapVessel.eachLayer(function (layer) {
                    mapVessel.removeLayer(layer);
                });
            }
        }
        overlayMaps = null;
        overlayMaps = {};
        myCreateVesselGroup(liVessels, liLastVesselPos)
        overlayMaps['Vessels'] = myVessels;

        if (sLeafletWmsString.length > 1) {
            const arrLeaflet2 = sLeafletWmsString.split(',')
            let iLen = arrLeaflet2.length;
            for (let iCnt4 = 0; iCnt4 < iLen; iCnt4 += 2) {
                sLayerName = null;
                sLayerName = arrLeaflet2[iCnt4].toString();
                wmsOptions = null;
                wmsOptions = {};
                wmsOptions.layers = sLayerName;
                wmsOptions.format = 'image/png';
                wmsOptions.transparent = true;
                wmsOptions.opacity = 0.5;
                sUrl = null;
                sUrl = arrLeaflet2[iCnt4 + 1];
                myWmsLayer = null
                myWmsLayer = new L.tileLayer.wms(sUrl, wmsOptions);
                overlayMaps[sLayerName] = myWmsLayer;
            }
        }
    }
    catch (err) {
        alert("Error in function myCreateOverlay: " + err.message);
    }
}


function myCreateBaseLayers() {
    try {
        layerNameArr = null;
        layerLinkArr = null;
        baseMaps = null;
        layerNameArr = [];
        layerLinkArr = [];
        baseMaps = []
        layerNameArr.push('OpenStreetMap');
        layerLinkArr.push(L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', { maxZoom: 19, attribution: 'OpenStreetMap', transparent: true }));
        for (iCn2 = 0; iCn2 < layerLinkArr.length; iCn2++) {
            baseMaps[layerNameArr[iCn2]] = layerLinkArr[iCn2];
        }
    }
    catch (err) {
        alert("Error in function myCreateBaseLayers: " + err.message);
    }
}


function myAddVesselMarker(sTitle, dLat, dLon) {
    try {
        var markerOptions = {};
        markerOptions.title = sTitle;
        markerOptions.clickable = true;
        markerOptions.draggable = false;
        var sPopupText = "Lat: " + dLat.toString() + ", Lon: " + dLon.toString();
        vesselVar.push(L.marker([dLat, dLon], markerOptions).bindPopup(sPopupText));
    }
    catch (err) {
        alert("Error in function myAddVesselMarker: " + err.message);
    }
}


function myCreateMap(dLatCenter, dLonCenter) {
    try {
        if (mapVessel !== null) {
            mapVessel.invalidateSize();
            mapVessel.off();
            mapVessel.remove();
        }
        mapVessel = null;
        layerControl = null;
        mapVessel = new L.map('map', { center: [dLatCenter, dLonCenter], zoom: 13, layers: [layerLinkArr[0]] });
        layerControl = new L.control.layers(baseMaps, overlayMaps).addTo(mapVessel);
    }
    catch (err) {
        alert("Error in function myCreateMap: " + err.message);
    }
}

问题根源

每次切换车辆时都完全销毁并重建了Leaflet地图实例和图层控件。切回原车辆时,图层控件仅保留了名称的选中标记,但新创建的WMS图层实例与控件的选中状态没有关联,导致地图上未添加对应的新图层。此外,myCreateOverlay中移除所有图层的逻辑会删掉底图,造成不必要的闪烁和状态丢失。

修复方案

1. 新增状态保存变量

添加变量记录当前选中的overlay图层名称:

// 保存当前选中的overlay图层名称
var selectedOverlayLayers = [];

2. 修改myOnSelectedVessel函数

调整执行顺序,复用现有地图并恢复选中状态:

function myOnSelectedVessel() {
    let sSelectedVessel = $('#sSelectedVessel').find(":selected").text()
    if (sSelectedVessel.length > 0) {
        $.ajax({
            url: '/vessel_map_set_vessel_id',
            data: {
                'sSelectedVesselName': sSelectedVessel,
            },
            type: 'POST',
            dataType: 'json',
            success: function (response) {
                if (response.result) {
                    alert(response.msg);
                }
                // 保存当前选中状态(地图已存在时)
                if (mapVessel && layerControl) {
                    selectedOverlayLayers = [];
                    Object.keys(overlayMaps).forEach(layerName => {
                        if (mapVessel.hasLayer(overlayMaps[layerName])) {
                            selectedOverlayLayers.push(layerName);
                        }
                    });
                }
                // 仅初始化一次底图
                if (!baseMaps) {
                    myCreateBaseLayers();
                }
                // 创建新的overlay图层
                myCreateOverlay(response.liVessels, response.liLastVesselPos, response.arrLeaflet.toString());
                // 创建或更新地图
                if (!mapVessel) {
                    myCreateMap(response.lat, response.lon);
                } else {
                    // 更新图层控件的overlay图层
                    layerControl.remove();
                    layerControl = L.control.layers(baseMaps, overlayMaps).addTo(mapVessel);
                    // 恢复选中状态
                    selectedOverlayLayers.forEach(layerName => {
                        if (overlayMaps[layerName]) {
                            overlayMaps[layerName].addTo(mapVessel);
                        }
                    });
                    // 平移到目标位置
                    mapVessel.panTo(new L.LatLng(parseFloat(response.lat), parseFloat(response.lon)));
                }
            },
            error: function (error) {
                alert("error in function vessel_map.myOnSelectedVessel");
            }
        });
    }
}

3. 修改myCreateOverlay函数

仅清理旧的overlay图层,保留底图:

function myCreateOverlay(liVessels, liLastVesselPos, sLeafletWmsString) {
    try {
        // 清理旧的overlay图层(从地图上移除)
        if (mapVessel && overlayMaps) {
            Object.values(overlayMaps).forEach(layer => {
                if (mapVessel.hasLayer(layer)) {
                    mapVessel.removeLayer(layer);
                }
            });
        }
        overlayMaps = {};
        myCreateVesselGroup(liVessels, liLastVesselPos)
        overlayMaps['Vessels'] = myVessels;

        if (sLeafletWmsString.length > 1) {
            const arrLeaflet2 = sLeafletWmsString.split(',')
            let iLen = arrLeaflet2.length;
            for (let iCnt4 = 0; iCnt4 < iLen; iCnt4 += 2) {
                sLayerName = arrLeaflet2[iCnt4].toString();
                wmsOptions = {
                    layers: sLayerName,
                    format: 'image/png',
                    transparent: true,
                    opacity: 0.5
                };
                sUrl = arrLeaflet2[iCnt4 + 1];
                myWmsLayer = new L.tileLayer.wms(sUrl, wmsOptions);
                overlayMaps[sLayerName] = myWmsLayer;
            }
        }
    }
    catch (err) {
        alert("Error in function myCreateOverlay: " + err.message);
    }
}

4. 修改myCreateMap函数

仅在地图不存在时创建,并默认选中船舶图层:

function myCreateMap(dLatCenter, dLonCenter) {
    try {
        mapVessel = new L.map('map', { center: [dLatCenter, dLonCenter], zoom: 13, layers: [layerLinkArr[0]] });
        layerControl = new L.control.layers(baseMaps, overlayMaps).addTo(mapVessel);
        // 默认选中Vessels图层
        if (overlayMaps['Vessels']) {
            overlayMaps['Vessels'].addTo(mapVessel);
            selectedOverlayLayers.push('Vessels');
        }
    }
    catch (err) {
        alert("Error in function myCreateMap: " + err.message);
    }
}

内容的提问来源于stack exchange,提问作者user10028792

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 08:01:28