[Leaflet]レイヤを乗算で重ねる(タイルレイヤの乗算合成)

このBlogとかでLeafletを使ったコンテンツを作るようになってから数年経って念願だった、タイルレイヤを乗算合成することが出来ましたので、ここに実装方法をメモしてこきます。これまで何度となく「Leaflet 乗算」で検索したのですが、最上位に表示されたのはこのBlogの「(OpenLayersは)乗算でレイヤを重ねられたのは良かったのですけど・・・」という部分で絶望的な気分になっていました。

Leaflet 乗算での検索結果(2021.5.9)

ところが、AMeDAS観測による複数の気象要素とレーダー画像を一枚の地図で見れるページを作っているなかで、Leaflet上で表示される地物はすべてDivタグの中に含まれていること、CSSにレイヤの合成モードを指定するプロパティ(mix-blend-mode)があることなどが分かってきて、最終的に乗算合成することが出来るようになったのでした。

さて、前置きはこの辺にして本題に入ります。

Leafletでレイヤを乗算合成したい場合、下記の手順で実装できました。
なお、CSSのmix-blend-modeがIEでは使えないそうなので、閲覧環境によっては無効となります。

  1. マップ(map)からペイン(pane)を作成し、返り値(HTMLElement)を保存しておく。
  2. 1で保存したHTMLElementのStyleを変更する。
  3. 乗算合成したいレイヤに1で作成したペインを指定する。

早速、サンプルソースを載せてみます。

<a href="javascript:SwitchMixBlendMode()">描画モードを変更</a>
<div id="leafletmap">
<script>
    //地図を生成(初期表示の中心座標,ズームレベル,レイヤなどをオプションで設定)
    let map = L.map('leafletmap', {center: [35.885870,139.536618], zoom: 15});
    //
    //ペイン作成→乗算合成
	let pnMulti = map.createPane("Multiply");
	pnMulti.style.mixBlendMode = "multiply";
	//
    //切り替え可能とするタイルレイヤを生成(タイルURL,ズーム範囲,著作権表示などをオプションで設定)
    let std = L.tileLayer('http://cyberjapandata.gsi.go.jp/xyz/std/{z}/{x}/{y}.png', {
        minZoom: 5, maxZoom: 18, 
        attribution: "地理院タイル"
        });
    let pal = L.tileLayer('http://cyberjapandata.gsi.go.jp/xyz/pale/{z}/{x}/{y}.png', {
        minZoom: 5, maxZoom: 18, 
        attribution: "地理院タイル"
        });
    let blk = L.tileLayer('http://cyberjapandata.gsi.go.jp/xyz/blank/{z}/{x}/{y}.png', {
        minZoom: 5, maxZoom: 14, 
        attribution: "地理院タイル"
        });
    let ort = L.tileLayer('http://cyberjapandata.gsi.go.jp/xyz/ort/{z}/{x}/{y}.jpg', {
        minZoom: 5, maxZoom: 18, 
        attribution: "地理院タイル"
        });
    let rapid = L.tileLayer('https://habs.dc.affrc.go.jp/rapid16/{z}/{x}/{y}.png', {
        tms: true, opacity: 1.0,
        minZoom: 8, maxZoom: 16, 
        attribution: "迅速測図(農業環境技術研究所)"
        });
    let flood2019 = L.tileLayer('https://cyberjapandata.gsi.go.jp/xyz/20191012typhoon19_arakawa_1014dansaizu/{z}/{x}/{y}.png', {
        minZoom: 5, maxZoom: 15, opacity: 1.0,
        pane: pnMulti,
        attribution: "地理院タイル"
        });
    // 
    //最初に表示するレイヤ
    map.addLayer(pal);
    map.addLayer(flood2019);
    // 
    //レイヤコントロールに切り替え可能とするレイヤを追加
    L.control.layers({
        '地理院タイル(標準地図)': std,
        '地理院タイル(淡色地図)': pal,
        '地理院タイル(写真)': ort,
        '明治期迅速測図': rapid
        }, 
        {
        '浸水判読図(令和元年台風第19号)': flood2019
        }, {collapsed: true}).addTo(map);
    //
    //スケールコントロールを追加(オプションはフィート単位を非表示)
    L.control.scale({imperial: false}).addTo(map);
    //
    //レイヤ透過モードを切り替える
    function SwitchMixBlendMode(){
    	let el = document.getElementsByClassName('leaflet-pane leaflet-Multiply-pane');
    	for(i=0;i<el.length;i++){
    		if(el[i].style.mixBlendMode == "multiply"){el[i].style.mixBlendMode = "normal";}
    		else{el[i].style.mixBlendMode = "multiply"; }
    	}
    }
</script>
</div>

まず、8行目で「Multiply」という名前のペインを作成(名前は任意)するとともに、帰り値を「pnMulti」という変数に保存しています。
次に、9行目でpnMultiのスタイルを乗算にしています。pnMultiはHTMLElementオブジェクトなので、「pnMulti.style.~」で合成モード以外のスタイルも当然ながら指定(変更)出来ます。例えばz-indexでレイヤの表示順を指定することも出来ます(参考:Leafletでのデフォルトのz-index(Leaflet公式サイト))。
最後に、35行目で乗算合成したいレイヤのペインに「pnMulti」を指定することで、乗算合成を有効にしています。

なお、1行目にあるリンクを押すと58~64行目の「SwitchMixBlendMode()」が呼び出されます。
Leafletのペインの設定は、CSSのクラス「leaflet-pane leaflet-{ペイン名称}-pane」に書き込まれるようです。今回のペイン名称は8行目の通り「Multiply」なのでCSSのクラス名称は「leaflet-pane leaflet-Multiply-pane」となります。
そのため、クラス「leaflet-pane leaflet-Multiply-pane」の要素を列挙(60~63行目)して、乗算の場合はnormalに、乗算以外の場合はmultiplyとするようにしています。

描画モードを変更