Leaflet中两张地图间GeoJSON数据的联动高亮实现需求
实现跨地图GeoJSON要素悬停联动高亮
要实现悬停第二张地图的GeoJSON对象时自动高亮第一张地图中start_fid匹配的要素,我们需要调整事件处理逻辑,同时优化要素查找效率,具体步骤如下:
关键修改点
- 预构建要素映射:提前将两个GeoJSON图层的要素按
start_fid存入对象,避免每次悬停时遍历整个图层查找 - 修改悬停事件处理:在第二张地图要素的
mouseover事件中,通过映射找到第一张地图的对应要素并高亮;mouseout时重置两个要素的样式 - 优化样式重置逻辑:确保能精准重置对应要素的样式,而不是只操作当前触发图层
修改后的完整代码
<!DOCTYPE html> <html> <head> <link href="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.5.1/leaflet.css" rel="stylesheet"/> <script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.5.1/leaflet.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script> <style> #mapid { height: 100%; width: 50%; left: 0; position: fixed; z-index: 0; } #mapid2 { height: 100%; width: 50%; right: 0; position: fixed; z-index: 0; } </style> </head> <body> <div id="mapid"></div> <div id="mapid2"></div> <script> var dat1 =({ "type" : "FeatureCollection", "name" : "NewFeatureType", "features" : [ { "type" : "Feature", "geometry" : { "type" : "Polygon", "coordinates" : [ [ [ 7.2518485112, 47.1390738345 ], [ 7.2540323762, 47.1390773819 ], [ 7.2540289251, 47.1400723508 ], [ 7.2518450195, 47.1400688032 ], [ 7.2518485112, 47.1390738345 ] ] ] }, "properties" : { "start_fid" : "504748" } }, { "type" : "Feature", "geometry" : { "type" : "Polygon", "coordinates" : [ [ [ 7.2565390484, 47.130837227 ], [ 7.2633719322, 47.1308479108 ], [ 7.2633626773, 47.1336579369 ], [ 7.2565294346, 47.1336472526 ], [ 7.2565390484, 47.130837227 ] ] ] }, "properties" : { "start_fid" : "862126" } } ] }); var dat2 =({ "type" : "FeatureCollection", "name" : "NewFeatureType", "features" : [ { "type" : "Feature", "geometry" : { "type" : "Point", "coordinates" : [ -19.9169596449, 32.1396903724 ] }, "properties" : { "start_fid" : "504748" } }, { "type" : "Feature", "geometry" : { "type" : "Point", "coordinates" : [ -19.924214327, 32.1530002773 ] }, "properties" : { "start_fid" : "862126" } } ] }); // 预构建要素映射,按start_fid存储图层 const featureMap1 = {}; const featureMap2 = {}; var mymap = L.map('mapid').setView([47.132,7.260], 15); L.tileLayer('https://api.mapbox.com/styles/v1/mtheisen/citei76iz005f2jpac8g7ilcl/tiles/256/{z}/{x}/{y}?access_token=pk.eyJ1IjoibXRoZWlzZW4iLCJhIjoiY2l0ZWk0NTBoMDVmMDJ0bXlyNmxzZ3RnMyJ9.fErHtcHVcTsHCLMPN1ZcQg', { attribution: 'Map data © <a href="http://openstreetmap.org">OpenStreetMap</a> contributors, <a href="http://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>, Imagery © <a href="http://mapbox.com">Mapbox</a> ', maxZoom: 18, id: 'mtheisen', accessToken: 'pk.eyJ1IjoibXRoZWlzZW4iLCJhIjoiY2l0ZWk0NTBoMDVmMDJ0bXlyNmxzZ3RnMyJ9.fErHtcHVcTsHCLMPN1ZcQg' }).addTo(mymap); var geojson1 = L.geoJson(dat1, { style: style, onEachFeature: function(feature, layer) { const fid = feature.properties.start_fid; featureMap1[fid] = layer; // 存入映射 layer._leaflet_id = fid; layer.on({ mouseover: highlightFeature, mouseout: resetHighlight, click: zoomTo }); // 原列表生成逻辑保留 $('#mapid2').append('<li data-value="' + fid + '">'+fid+'</li>'); } }).addTo(mymap); var mymapid2 = L.map('mapid2',{ zoomControl:false }).setView([32.1378527851,-19.9229088608], 14); var geojson2 = L.geoJson(dat2, { pointToLayer: function (feature, latlng) { return L.circleMarker(latlng); }, style: style, onEachFeature: function(feature, layer) { const fid = feature.properties.start_fid; featureMap2[fid] = layer; // 存入映射 layer._leaflet_id = fid; // 这里修改mouseover和mouseout处理,实现跨地图联动 layer.on({ mouseover: function(e) { const currentLayer = e.target; const fid = currentLayer.feature.properties.start_fid; // 高亮当前图层和第一张地图的对应图层 currentLayer.setStyle(highlightStyle); if (featureMap1[fid]) { featureMap1[fid].setStyle(highlightStyle); } }, mouseout: function(e) { const currentLayer = e.target; const fid = currentLayer.feature.properties.start_fid; // 重置当前图层和第一张地图的对应图层 geojson2.resetStyle(currentLayer); if (featureMap1[fid]) { geojson1.resetStyle(featureMap1[fid]); } }, click: zoomTo }); } }).addTo(mymapid2); function style(feature) { return { fillColor: "red", weight: 1, opacity: 1, color: 'red', dashArray: '3', fillOpacity: 0.7 }; } var highlightStyle = { fillColor: "yellow", weight: 2, opacity: 1, color: 'yellow', dashArray: '3', fillOpacity: 0.7 }; function highlightFeature(e) { var layer = e.target; layer.setStyle(highlightStyle); // 如果需要悬停第一张地图时也联动第二张,可以添加以下代码 // const fid = layer.feature.properties.start_fid; // if (featureMap2[fid]) { // featureMap2[fid].setStyle(highlightStyle); // } } function resetHighlight(e) { var layer = e.target; geojson1.resetStyle(layer); // 如果需要悬停第一张地图移出时联动重置第二张,可以添加以下代码 // const fid = layer.feature.properties.start_fid; // if (featureMap2[fid]) { // geojson2.resetStyle(featureMap2[fid]); // } } function zoomTo(e) { var layer = e.target; // 判断当前图层属于哪个地图,然后缩放对应的地图 if (layer._map === mymap) { mymap.fitBounds(layer.getBounds()); } else if (layer._map === mymapid2) { mymapid2.fitBounds(layer.getBounds()); } } // 保留原列表悬停联动逻辑 var hovered_id, layer, layer2; $('#mapid2 li').on('mouseenter', function(e){ hovered_id = $(e.target).data('value'); layer = geojson1.getLayer(hovered_id); layer2 = geojson2.getLayer(hovered_id); layer.setStyle(highlightStyle); layer2.setStyle(highlightStyle); }).on('mouseout', function(e){ geojson1.resetStyle(layer); geojson2.resetStyle(layer2); }); </script> </body> </html>
代码解释
- 要素映射:我们创建了
featureMap1和featureMap2两个对象,分别存储第一张和第二张地图的要素,键为start_fid,值为对应的Leaflet图层对象,这样可以在O(1)时间内找到匹配的要素。 - 第二张地图的悬停事件:在
onEachFeature里为第二张地图的要素绑定了自定义的mouseover和mouseout事件,当鼠标悬停时,通过start_fid从featureMap1中找到对应图层并高亮;移出时重置两个图层的样式。 - 可选的反向联动:如果需要悬停第一张地图时也高亮第二张的对应要素,可以取消
highlightFeature和resetHighlight函数里的注释代码。 - 优化缩放逻辑:修改了
zoomTo函数,判断当前图层所属的地图,然后缩放对应的地图,避免缩放错误。
内容的提问来源于stack exchange,提问作者vogis
相关产品推荐
相关产品推荐

