Leaflet:两个底图层之间的混合

6
我想创建一个Leaflet地图,可以在不同的地图层之间切换。这当然不是问题。
但我想添加一个滑块,可以在实际地图层和以前的地图层之间进行混合,以便比较这两个地图的内容:
右滑杆 => 实际地图的100%不透明度
左滑杆 => 实际地图的0%不透明度,也就是以前的地图的100%不透明度
中间滑杆 => 实际地图的50%不透明度,意味着以前的地图背景也会透过50%

我已经成功实现了初始加载地图后的混合效果。但当我从地图选择器菜单中选择另一张地图时,无法获得混合效果。应该显示在背景中的以前的地图层似乎已经丢失了。
我认为问题在于当我通过“map.addLayer(bgMap);”将前一个地图层添加到背景中时,函数“fct_layerchange”不仅被事件监听器“map.on”调用,还被调用。
我不知道如何解决这些不必要的函数“多次调用”的问题,因为我依赖于选择新底图时的“baselayerchange”事件。而且当在该事件函数内部时,此事件会再次触发。有没有Leaflet专家有什么好主意可以帮我解决这个问题呢? :-)

<!DOCTYPE html> 
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Blend 2 maps</title>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.0.3/dist/leaflet.css" />
<script src="https://unpkg.com/leaflet@1.0.3/dist/leaflet.js"></script>
<style>
    html, body, #map {height: 100%;}
</style>
</head>

<body>
<div id="map"></div>
<script>

// 1.) BASEMAPS
var osm_mapnik = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {subdomains:'abc', maxZoom:19, noWrap:true, attribution:'<a href="http://www.openstreetmap.org">Openstreetmap</a> | <a href="http://www.openstreetmap.org/copyright/">OpenStreetMap</a>' });
var thunder_cycle = L.tileLayer('https://{s}.tile.thunderforest.com/cycle/{z}/{x}/{y}.png', {subdomains:'abc', maxZoom:19, noWrap:true, attribution:'<a href="http://www.thunderforest.com">Thunderforest</a> | <a href="http://www.openstreetmap.org/copyright/">OpenStreetMap</a>' });
var thunder_outdoors = L.tileLayer('https://{s}.tile.thunderforest.com/outdoors/{z}/{x}/{y}.png', {subdomains:'abc', maxZoom:19, noWrap:true, attribution:'<a href="http://www.thunderforest.com">Thunderforest</a> | <a href="http://www.openstreetmap.org/copyright/">OpenStreetMap</a>' });
var thunder_pioneer = L.tileLayer('https://{s}.tile.thunderforest.com/pioneer/{z}/{x}/{y}.png', {subdomains:'abc', maxZoom:19, noWrap:true, attribution:'<a href="http://www.thunderforest.com">Thunderforest</a> | <a href="http://www.openstreetmap.org/copyright/">OpenStreetMap</a>' });

// 2.) OVERLAYMAPS
var heidel_bound = L.tileLayer('http://korona.geog.uni-heidelberg.de/tiles/adminb/x={x}&y={y}&z={z}', {maxNativeZoom: 18, maxZoom:19, noWrap:true, attribution:'<a href="http://korona.geog.uni-heidelberg.de/contact.html">Uni-Heidelberg</a>' });
var wmt_hiking = L.tileLayer('https://tile.waymarkedtrails.org/hiking/{z}/{x}/{y}.png', {maxNativeZoom: 18, maxZoom:19, noWrap:true, attribution:'<a href="http://waymarkedtrails.org">Waymarkedtrails</a>' });
 
// LAYERMENU
var baseMaps = {
    "OpenStreetMap Mapnik": osm_mapnik,
    "Thunderforest Opencycle": thunder_cycle,
    "Thunderforest Outdoors": thunder_outdoors,
    "Thunderforest Pioneer": thunder_pioneer
};

var map = L.map ( 'map', { center: [47, 15], zoom: 11, layers: [thunder_cycle, wmt_hiking] } );
var overlayMaps = {
    "Hiking Routes": wmt_hiking,
    "Boundaries": heidel_bound,
};
var ctr_mapLayers = L.control.layers(baseMaps, overlayMaps).addTo(map);
var fgMap = thunder_cycle;
var bgMap = thunder_pioneer;
map.addLayer(bgMap);            // add initial backgroundmap-layer to map
bgMap.bringToBack();            // move backgroundmap-Layer to to the background of the map

function fct_blend() {
  valBlend = document.getElementById("id_sliderBlend").value;
  document.getElementById("id_valBlend").innerHTML = Number(valBlend).toFixed(1)
  fgMap.setOpacity(valBlend);
}
 
var ctr_blend = L.control();
ctr_blend.onAdd = function (map) {
    valOpacity = 1.0;
    this.div = L.DomUtil.create('div');
    this.div.innerHTML = '<span id="id_valBlend">1.0</span><input type="range" id="id_sliderBlend" min="0" max="1" step="0.1" value="1" style="width:100px;" oninput="fct_blend()">';
    L.DomEvent.disableClickPropagation(this.div);
    return this.div;
};
ctr_blend.addTo(map);

var fct_layerchange = function (e) {
    bgMap = fgMap;
    bgMap.setOpacity(1);         // set opacity of former foregroundmap-layer which is now background-layer to 1.0;
    map.addLayer(bgMap);         // add former foregroundmap-layer as backgroundmap-layer to map again. 
    fgMap = e.layer;             // update fgMap-variable with the actual foregroundmap-layer
    fgMap.setOpacity(valBlend);  // set opacity of the new foregroundmap-layer to the actual blend-Value.
};

map.on('baselayerchange', fct_layerchange);

</script>
</body>
</html>


你可能会对 Leaflet.OpacityControls 插件感兴趣。它可以帮助设置透明度,但你仍需要弄清如何更改图层并控制当前图层的透明度。 - ghybs
1个回答

7
我想告诉您,最终我找到了一个可行的解决方案。经过数小时的试错和研究Leaflet源代码,我发现了问题所在以及如何解决。
在我的情况下,问题是Leaflet的图层控制对象严格管理每个底图和叠加图层(= 同一时间只能显示一个底图层)。如果我添加另一个用于背景的底图层,则图层控制对象将立即将其删除,即使我已将其放置在另一个面板上。
解决方法是:为每个底图创建一个单独的副本(例如"osm_mapnik"将获得一个名为"osm_mapnik_bg"的副本),用于在后台面板上使用。原因是图层控制对象仅控制"osm_mapnik",但我完全控制"osm_mapnik_bg":-)
因此,图层控件管理前景地图的更改,而我管理背景地图的更改-劳动分工完美。

<!DOCTYPE html> 
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Blend 2 maps</title>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.0.3/dist/leaflet.css" />
<script src="https://unpkg.com/leaflet@1.0.3/dist/leaflet.js"></script>
<style>
    html, body, #map { height: 100%; }
    html, body, #map { margin: 0; padding: 0; }
</style>
</head>

<body>
<div id="map"></div>
<script>

// 1.) BASEMAPS
var osm_mapnik = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {subdomains:'abc', maxZoom:19, noWrap:true, attribution:'<a href="http://www.openstreetmap.org">Openstreetmap</a> | <a href="http://www.openstreetmap.org/copyright/">OpenStreetMap</a>' });
var thunder_cycle = L.tileLayer('https://{s}.tile.thunderforest.com/cycle/{z}/{x}/{y}.png', {subdomains:'abc', maxZoom:19, noWrap:true, attribution:'<a href="http://www.thunderforest.com">Thunderforest</a> | <a href="http://www.openstreetmap.org/copyright/">OpenStreetMap</a>' });
var thunder_outdoors = L.tileLayer('https://{s}.tile.thunderforest.com/outdoors/{z}/{x}/{y}.png', {subdomains:'abc', maxZoom:19, noWrap:true, attribution:'<a href="http://www.thunderforest.com">Thunderforest</a> | <a href="http://www.openstreetmap.org/copyright/">OpenStreetMap</a>' });
var thunder_pioneer = L.tileLayer('https://{s}.tile.thunderforest.com/pioneer/{z}/{x}/{y}.png', {subdomains:'abc', maxZoom:19, noWrap:true, attribution:'<a href="http://www.thunderforest.com">Thunderforest</a> | <a href="http://www.openstreetmap.org/copyright/">OpenStreetMap</a>' });

// 1b.) BASEMAPS copies for use on background pane: use 'mapPane' which's z-index is lower (=behind) the 'tilePane' used by the layers in 1.)
var osm_mapnik_bg = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {pane:'mapPane', subdomains:'abc', maxZoom:19, noWrap:true, attribution:'<a href="http://www.openstreetmap.org">Openstreetmap</a> | <a href="http://www.openstreetmap.org/copyright/">OpenStreetMap</a>' });
var thunder_cycle_bg = L.tileLayer('https://{s}.tile.thunderforest.com/cycle/{z}/{x}/{y}.png', {pane:'mapPane', subdomains:'abc', maxZoom:19, noWrap:true, attribution:'<a href="http://www.thunderforest.com">Thunderforest</a> | <a href="http://www.openstreetmap.org/copyright/">OpenStreetMap</a>' });
var thunder_outdoors_bg = L.tileLayer('https://{s}.tile.thunderforest.com/outdoors/{z}/{x}/{y}.png', {pane:'mapPane', subdomains:'abc', maxZoom:19, noWrap:true, attribution:'<a href="http://www.thunderforest.com">Thunderforest</a> | <a href="http://www.openstreetmap.org/copyright/">OpenStreetMap</a>' });
var thunder_pioneer_bg = L.tileLayer('https://{s}.tile.thunderforest.com/pioneer/{z}/{x}/{y}.png', {pane:'mapPane', subdomains:'abc', maxZoom:19, noWrap:true, attribution:'<a href="http://www.thunderforest.com">Thunderforest</a> | <a href="http://www.openstreetmap.org/copyright/">OpenStreetMap</a>' });

// 2.) OVERLAYMAPS
var heidel_bound = L.tileLayer('http://korona.geog.uni-heidelberg.de/tiles/adminb/x={x}&y={y}&z={z}', {maxNativeZoom: 18, maxZoom:19, noWrap:true, attribution:'<a href="http://korona.geog.uni-heidelberg.de/contact.html">Uni-Heidelberg</a>' });
var wmt_hiking = L.tileLayer('https://tile.waymarkedtrails.org/hiking/{z}/{x}/{y}.png', {maxNativeZoom: 18, maxZoom:19, noWrap:true, attribution:'<a href="http://waymarkedtrails.org">Waymarkedtrails</a>' });
 
// LAYERMENU
var baseMaps = {
    "OpenStreetMap Mapnik": osm_mapnik,
    "Thunderforest Opencycle": thunder_cycle,
    "Thunderforest Outdoors": thunder_outdoors,
    "Thunderforest Pioneer": thunder_pioneer
};

// needed to get the layer's Objectname by its Layer-Controlname which is the only name passed by the 'baselayerchange'-event  
var layerLookup = {"OpenStreetMap Mapnik":"osm_mapnik", "Thunderforest Opencycle":"thunder_cycle", "Thunderforest Outdoors":"thunder_outdoors", "Thunderforest Pioneer":"thunder_pioneer",};

var map = L.map ( 'map', { center: [47, 15], zoom: 11, layers: [thunder_cycle] } );
var overlayMaps = {
    "Hiking Routes": wmt_hiking,
    "Boundaries": heidel_bound,
};
var ctr_mapLayers = L.control.layers(baseMaps, overlayMaps).addTo(map);

var fgLayerControlname = "Thunderforest Opencycle";       // default foreground-Layer*Controlname*
var fgLayer = window [layerLookup [fgLayerControlname]];  // default foreground-Layer Object
var bgLayerName = 'thunder_pioneer_bg';                   // default background-Layer*Objectname*
var bgLayer = thunder_pioneer_bg;                         // default background-Layer Object
map.addLayer(bgLayer);        

function fct_blend() {
  valBlend = document.getElementById("id_sliderBlend").value;
  document.getElementById("id_valBlend").innerHTML = Number(valBlend).toFixed(1)
  fgLayer.setOpacity(valBlend);
}
 
var ctr_blend = L.control({position:'bottomright'});
ctr_blend.onAdd = function (map) {
    valBlend = 1.0;
    this.div = L.DomUtil.create('div');
    this.div.innerHTML = '<span id="id_valBlend">1.0</span><input type="range" id="id_sliderBlend" min="0" max="1" step="0.1" value="1" style="width:100px;" oninput="fct_blend()">';
    L.DomEvent.disableClickPropagation(this.div);
    return this.div;
};
ctr_blend.addTo(map);

var fct_layerchange = function (e) {
    map.removeLayer(bgLayer);                                   // remove former bg-Layer
    bgLayerName = [layerLookup [fgLayerControlname]] + '_bg';   // set Object*name* of new bg-Layer which is former fg-Layer
    bgLayer = window[bgLayerName];                              // set bgLayer-Object out of its Object*name* 
    map.addLayer(bgLayer);                                      // add former foregroundmap-layer as backgroundmap-layer to map again. 
    fgLayerControlname = e.name;                                // get fg-Layer *Controlname* which is used in the Layercontrol-menu (its Objectname is not passed by the event)
    fgLayer = window [layerLookup [fgLayerControlname]];        //set fgLayer-Object by the use of its Objectname which is derived from its *Controlname*
    fgLayer.setOpacity(valBlend);  // set opacity of the new foreground-layer to the actual blend-Value.
};

map.on('baselayerchange', fct_layerchange);                     // fired if a new maplayer is choosen by the Layercontrol

</script>
</body>
</html>


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