このエントリの続きです:
Leaflet.js の UI で画像を表示する(1)


地図操作ライブラリの Leaflet.js の応用で、地図以外のオブジェクトを Leaflet の UI で表示する方法を紹介しています。前回は imageOverlay を使って画像を表示しましたが、今回は canvasOverlay を使ってキャンバス(HTML5 の <canvas>)を操作する方法を紹介します。比較の意味も含めて、表示内容そのものは前回同様に画像としますが、画像を画像のまま表示するのではなく、画像をキャンバスに描画した上で、更にキャンバスに描画を加える、という内容を紹介します。必要に応じて前回の内容も参照しながら見てください。

なお今回も表示対象画像は「いらすとや」「家にいるアマビエ」の画像とします:
amabie_stay_home


また前回紹介した内容は Leaflet.js の標準機能だけで実現しましたが、今回はアドイン機能である L.CanvasLayer を併用して実現します。以下のサイトからあらかじめ L.CanvasLayer.js をダウンロードしておいてください:
https://raw.githubusercontent.com/Sumbera/gLayers.Leaflet/master/L.CanvasLayer.js


ダウンロードできたら以下のような HTML を用意します:
<script src="./L.CanvasLayer.js"></script>

<div id="demoMap"></div>

<script>
//. 空の地図を OpenStreetMap データで表示
map = L.map('demoMap', { dragging: true, zoomControl: true, minZoom: -5, maxZoom: 5, crs: L.CRS.Simple, preferCanvas: true } );

//. 表示する画像のパスとサイズ
var image = {
  url: './3600x3600.png',
  width: 3600,
  height: 3600
};

//. 画像領域を地図に設定
var imageBounds = L.latLngBounds([
  map.unproject( [ 0, image.height ] ),
  map.unproject( [ image.width, 0 ] )
]);
map.fitBounds( imageBounds );
map.setMaxBounds( imageBounds.pad( 0.5 ) );

//. 画像をオーバーレイで設定
L.imageOverlay( image.url, imageBounds ).addTo( map );

//. キャンバスレイヤーを設定
L.canvasLayer().delegate( this ).addTo( map );
      
//. キャンバスの再描画イベントハンドラ
function onDrawLayer( info ) {
  var canvas = info.canvas;
  var ctx = canvas.getContext( '2d' );

  var colors = [
    "rgba(0,0,0,0.2)",
    "rgba(255,0,0,0.2)"
  ];
  var points = [
    [ -1000, 2000 ],
    [ -2000, 2250 ]
  ];

  ctx.clearRect( 0, 0, canvas.width, canvas.height );

  //. ズームレベルに合わせて arc の描画半径を調整
  var zoom = map.getZoom();
  var r = 30 * Math.pow( 2, zoom );

  for( var i = 0; i < points.length; i++ ){
    var point = points[i];
    if( info.bounds.contains( [ point[0], point[1] ] ) ){
      var p = info.layer._map.latLngToContainerPoint( [ point[0], point[1] ] );
      ctx.beginPath();
      ctx.fillStyle = colors[i];
      ctx.arc( p.x, p.y, r, 0, Math.PI * 2 );
      ctx.fill();
      ctx.closePath();
    }
  }
}
</script>



まず L.CanvasLayer.js を読み込みます。そして L.imageOverlay を使って画像(3600x3600.png)をロードするのですが、ここまでは前回紹介したものと同様です。

その後で L.canvasLayer() でキャンバスをレイヤーとして扱います。これでキャンバスの再描画イベントハンドラである onDrawLayer() をカスタマイズすることで画像に更にグラフィックをカスタマイズすることが可能になります。

このコードが実際に動いているワーキングデモページはこちらです:
https://dotnsf.github.io/leaflet_overlay2/


上記 URL をブラウザで開くと、前回同様「家にいるアマビエ」の画像が Leaflet の UI で表示されます:
2020050401


左上のズームコントローラーやスマホのピンチイン/アウト操作で画像が拡大縮小されます。実は前回と少しだけ違う点があることに気付く方はいらっしゃるでしょうか?:
2020050402


よく見ると窓とアマビエの右目尻に丸いオブジェクトが描画されています。これはゴミではなく、画像が表示されている Canvas の上にグラフィックコンテキストを使って(JavaScript で)描画されたものです:
2020050403


画面を更に拡大/縮小しても、ズームレベルに合わせて表示されます:
2020050404


この部分は上記コードの OnDrawLayer 関数内で処理されています。キャンバスが再描画されるタイミングで呼び出されますが、パラメータ(info)の info.canvas で表示されている <canvas> 要素を取得することができます。なので後は info.canvas.getContext( '2d' ) でグラフィックコンテキストを取得した後にここなどを参照して描画すると上述のように元の画像の上に更にグラフィックを追加した上で Leaflet の UI で表示できるようになる、というものです。


<canvas> をある程度使ったことがあると、この方法が使えるのは本当に便利です。自由に描画したキャンバスを Leaflet.js の UI で表示できるようになるため、使いみちが広がります。