KTGIS.net
MANDARA
今昔マップ
研究室
Geocoding
サービス
災害関連



谷謙二研究室(埼玉大学教育学部人文地理学)

Leafletの使い方

LeafletはWeb地図サービスで広く使われるオープンソースのJavaScriptライブラリです。

ここでは、Leafletで地図を表示する方法をコードとともにわかりやすく解説します。

Google Maps APIの無料での利用に制限が多くなってきたので、Leafletに乗り換えたいという人の参考になれば幸いです。

なお、ここではバージョン1.3.0を使用しています。すべての機能を解説しているわけではないので、オブジェクトのメソッド、オプション、イベントなどは1.3.4のリファレンスを参考にしてください。

(2018/8/31基本編Step1-10公開、9/3 Step11-14、9/13 Step15、2020/4/1 Step16公開)


Step 12.タイルレイヤの重ね合わせ

  • タイルレイヤはオーバーレイにも設定できる
  • その際、ベースレイヤが見えるよう、透過度を指定するとよい
  • タイルレイヤにより最大ズームレベルが異なる場合は、maxNativeZoomを設定すると拡大して表示される
<!DOCTYPE html>
<html>

<head>
  <meta charset="UTF-8">
  <title>Step12.タイルレイヤの重ね合わせ|Lefletの応用|埼玉大学谷謙二研究室</title>
  <link rel="stylesheet" href="https://unpkg.com/leaflet@1.3.0/dist/leaflet.css" />
  <script src="https://unpkg.com/leaflet@1.3.0/dist/leaflet.js"></script>
  <script>
    function init() {
      var map = L.map('mapcontainer', { zoomControl: false });
      map.setView([35.40, 136], 10);
      var gsiattr = "<a href='http://portal.cyberjapan.jp/help/termsofuse.html' target='_blank'>地理院タイル</a>";
      var gsi = L.tileLayer('https://cyberjapandata.gsi.go.jp/xyz/std/{z}/{x}/{y}.png', { attribution: gsiattr });
      var gsipale = L.tileLayer('http://cyberjapandata.gsi.go.jp/xyz/pale/{z}/{x}/{y}.png', { attribution: gsiattr });
      var osm = L.tileLayer('http://tile.openstreetmap.jp/{z}/{x}/{y}.png',
          { attribution: "<a href='http://osm.org/copyright' target='_blank'>OpenStreetMap</a> contributors" });
      //オーバーレイ用のタイルレイヤ
      //opacityで透過度を設定、maxNativeZoomを指定すると、それ以上のズームレベルのタイルデータは、指定のズームレベルのタイル画像を拡大して表示される
      var gsirelief = L.tileLayer('http://cyberjapandata.gsi.go.jp/xyz/relief/{z}/{x}/{y}.png', { opacity: 0.7, maxNativeZoom: 15, attribution: gsiattr });
      var gsirehillshademap = L.tileLayer('http://cyberjapandata.gsi.go.jp/xyz/hillshademap/{z}/{x}/{y}.png', { opacity: 0.5, maxNativeZoom: 16, attribution: gsiattr });
      var baseMaps = {
        "地理院地図": gsi,
        "淡色地図": gsipale,
        "オープンストリートマップ": osm
      };
      var overlayMaps = {
        "色別標高図": gsirelief,
        "陰影起伏図": gsirehillshademap
      };
      L.control.layers(baseMaps, overlayMaps).addTo(map);
      gsi.addTo(map);
    }
  </script>
</head>

<body onload="init()">
  <div id="mapcontainer" style="position:absolute;top:0;left:0;right:0;bottom:0;"></div>
</body>

</html>

Step 13.地図画面上へのDiv要素の追加と位置の表示

  • Leafletの地図上に独自のDOM要素を追加することができる
  • 追加する際にidを設定しておくとgetElementByIdで後から操作できる
  • マウス位置は、mousemoveイベント時のMouseイベント、e.latlngプロパティで取得する
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>Step13.地図画面上へのDiv要素の追加と位置の表示|Lefletの応用|埼玉大学谷謙二研究室</title>
  <link rel="stylesheet" href="https://unpkg.com/leaflet@1.3.0/dist/leaflet.css" />
  <script src="https://unpkg.com/leaflet@1.3.0/dist/leaflet.js"></script>
  <style>
    /* 作成するdivのCSS */
    .infostyle {
      border: solid 1px;
      background-color: azure;
      border-radius: 10px;
      opacity: 0.8;
      padding: 5px;
      font-size: 15px;
      color: black;
    }
  </style>
  <script>
    function init() {
      var map = L.map('mapcontainer', { zoomControl: false });
      var mpoint = [35.40, 136];
      map.setView(mpoint, 5);
      L.tileLayer('https://cyberjapandata.gsi.go.jp/xyz/std/{z}/{x}/{y}.png', {
          attribution: "<a href='https://maps.gsi.go.jp/development/ichiran.html' target='_blank'>地理院タイル</a>"
      }).addTo(map);
      //mousemoveイベントを設定
      map.on('mousemove', onMapMousemove)
      //左上にdivコントロールを表示
      var latloninfo = L.control({ position: "topleft" });
      latloninfo.onAdd = function (map) {
        //divを作成
        this.ele = L.DomUtil.create('div', "infostyle");
        //後で使うためにidを設定
        this.ele.id = "latlondiv";
        //最初は非表示
        this.ele.style.visibility = "hidden";
        //div上のとmousemoveイベントがmapに連鎖しないように設定
        this.ele.onmousemove = function (e) { e.stopPropagation() };
        return this.ele;
      };
      latloninfo.addTo(map);
    }
    function onMapMousemove(e) {
      //地図上を移動した際にdiv中に緯度経度を表示
      var box = document.getElementById("latlondiv");
      var html = "緯度:" + e.latlng.lat.toFixed(6) + "<br>" +
        "経度:" + e.latlng.lng.toFixed(6);
      box.innerHTML = html;
      box.style.visibility = "visible";
    }
  </script>
</head>
<body onload="init()">
  <div id="mapcontainer" style="position:absolute;top:0;left:0;right:0;bottom:0;"></div>
</body>

</html>

Step 14.地図画面上へのselect要素の追加とベースレイヤの切り替え

  • ベースレイヤの選択は、Step4でやったようにLeaflet所定のコントロールからできるが、独自に作成することもできる
  • Step13と同様にセレクトボックスを地図上に配置し、選択してタイルレイヤをベースレイヤに設定するサンプルである
<!DOCTYPE html>
<html>

<head>
  <meta charset="UTF-8">
  <title>Step14.地図画面上へのselect要素の追加とベースレイヤの切り替え|Lefletの応用|埼玉大学谷謙二研究室</title>
  <link rel="stylesheet" href="https://unpkg.com/leaflet@1.3.0/dist/leaflet.css" />
  <script src="https://unpkg.com/leaflet@1.3.0/dist/leaflet.js"></script>
  <script>
    var map;
    var baseMaps;
    function init() {
      map = L.map('mapcontainer', { zoomControl: false });
      var mpoint = [35.40, 136];
      map.setView(mpoint, 5);
      var gsiattr = "<a href='http://portal.cyberjapan.jp/help/termsofuse.html' target='_blank'>地理院タイル</a>";
      var gsi = L.tileLayer('https://cyberjapandata.gsi.go.jp/xyz/std/{z}/{x}/{y}.png', { attribution: gsiattr });
      var gsipale = L.tileLayer('http://cyberjapandata.gsi.go.jp/xyz/pale/{z}/{x}/{y}.png', { attribution: gsiattr });
      var osm = L.tileLayer('http://tile.openstreetmap.jp/{z}/{x}/{y}.png',
        { attribution: "<a href='http://osm.org/copyright' target='_blank'>OpenStreetMap</a> contributors" });
      baseMaps = {
        "地理院地図": gsi,
        "淡色地図": gsipale,
        "オープンストリートマップ": osm
      };
      //左上にselectコントロールを表示
      var selBox = L.control({ position: "topleft" });
      selBox.onAdd = function (map) {
        //selectを作成
        this.ele = L.DomUtil.create('select');
        //後で使うためにidを設定
        this.ele.id = "selectBaseLayer";
        return this.ele;
      };
      selBox.addTo(map);
      //selectコントロールの中身を設定する
      var sel = document.getElementById("selectBaseLayer");
      sel.setAttribute("onchange", "baseLayerChage()");
      for (var key in baseMaps) {
        var opt = L.DomUtil.create('option', '', sel);
        //valueと表示文字にbaseMapsのキーを設定する
        opt.value = key;
        opt.innerText = key;
      }
      //最初のベースタイル
      baseLayerChage();
    }
    function baseLayerChage() {
      //既存のベースレイヤをremoveする
      for (var key in baseMaps) {
        if (map.hasLayer(baseMaps[key]) == true) {
          map.removeLayer(baseMaps[key]);
        }
      }
      //選択されたベースレイヤをmapに設定する
      var sel = document.getElementById("selectBaseLayer");
      var selTile = sel.options[sel.selectedIndex].value;
      baseMaps[selTile].addTo(map);
    }
  </script>

</head>

<body onload="init()">
  <div id="mapcontainer" style="position:absolute;top:0;left:0;right:0;bottom:0;"></div>
</body>

</html>

Step 15.複数マーカーの表示

  • Step5~6で行ったマーカーについて複数まとめて表示
  • 配列に座標とポップアップする文字を設定し、Forループで回してマーカーに設定
  • fitBoundsメソッドで、マーカーすべてが表示されるように地図領域を設定
<!DOCTYPE html>
<html>

<head>
    <meta charset="UTF-8">
    <title>Step15.複数マーカーの表示|Lefletの応用|埼玉大学谷謙二研究室</title>
    <link rel="stylesheet" href="https://unpkg.com/leaflet@1.3.0/dist/leaflet.css" />
    <script src="https://unpkg.com/leaflet@1.3.0/dist/leaflet.js"></script>
  <script>
    //マーカーに表示したい対象の緯度経度とポップアップする名称を設定
    var markerList = [
      { pos: [35.8645472, 139.6048663], name: "セブンイレブン浦和埼玉大学店" },
      { pos: [35.8689857, 139.6086909], name: "セブンイレブンさいたま大久保店" },
      { pos: [35.871305, 139.6128431], name: "ファミリーマート浦和上大久保店" },
      { pos: [35.8665389, 139.6133905], name: "ミニストップさいたま上大久保店" },
      { pos: [35.8650306, 139.6070633], name: "ローソン埼玉大学店" }
    ];
    function init() {
      var map = L.map('mapcontainer');
      L.tileLayer('https://cyberjapandata.gsi.go.jp/xyz/std/{z}/{x}/{y}.png', {
        attribution: "<a href='https://maps.gsi.go.jp/development/ichiran.html' target='_blank'>地理院タイル</a>"
      }).addTo(map);
      //マーカー全体が入るボックスを作る
      var bound = L.latLngBounds(markerList[0].pos, markerList[0].pos);
      //markerListの設定でマーカーを追加
      for (var num in markerList) {
        var mk = markerList[num];
        var popup = L.popup().setContent(mk.name);
        L.marker(mk.pos, { title: mk.name }).bindPopup(popup).addTo(map);
        //マーカー全体が入るボックスを広げる
        bound.extend(mk.pos);
      }
      //マーカー全体が入るように地図範囲を設定する
      map.fitBounds(bound);
    }
  </script>
</head>

<body onload="init()">
  <div id="mapcontainer" style="position:absolute;top:0;left:0;right:0;bottom:0;"></div>
</body>

</html>

Step 16.タイル座標をGridlayerで表示

  • タイル座標(z,x,y)をGridlayerメソッドを使って表示
  • Gridlayerで表示するHTML要素は、クラスを作っておく
  • L.control.layersでクラスをベースマップレイヤまたはオーバーレイレイヤに追加する
<!DOCTYPE html>
    <html>
    <head>
      <meta charset="UTF-8">
      <title>Step16.タイル座標をGridlayerで表示する|Lefletの応用|埼玉大学谷謙二研究室</title>
      <link rel="stylesheet" href="https://unpkg.com/leaflet@1.3.0/dist/leaflet.css" />
      <script src="https://unpkg.com/leaflet@1.3.0/dist/leaflet.js"></script>
      <script>
        function init() {
          var map = L.map('mapcontainer', { zoomControl: false });
          map.setView([35.40, 136], 0);
          map.setMaxZoom(20);
          var gsiattr = "<a href='http://portal.cyberjapan.jp/help/termsofuse.html' target='_blank'>地理院タイル</a>";
          var gsipale = L.tileLayer('http://cyberjapandata.gsi.go.jp/xyz/pale/{z}/{x}/{y}.png', { attribution: gsiattr });
          var osm = L.tileLayer('http://tile.openstreetmap.jp/{z}/{x}/{y}.png',
            { attribution: "<a href='http://osm.org/copyright' target='_blank'>OpenStreetMap</a> contributors" });
          var osmNoLabels = L.tileLayer('http://tiles.wmflabs.org/osm-no-labels/{z}/{x}/{y}.png',
            { attribution: "<a href='http://osm.org/copyright' target='_blank'>OpenStreetMap</a> contributors" });
          var baseMaps = {
            "地理院地図(淡色地図)": gsipale,
            "オープンストリートマップ": osm,
            "OSM no labels": osmNoLabels
          };
          //GridLayerClassを変数に設定し、オーバーレイのタイルレイヤに設定
          var zxyLayer = new GridLayerClass();
          L.control.layers(baseMaps, { "XYZ": zxyLayer }).addTo(map);
          osm.addTo(map);
          zxyLayer.addTo(map);
        }
    
        //zxyを表示するタイルレイヤのクラス
        var GridLayerClass = L.GridLayer.extend({
          createTile: function (coords) {
            //div要素でタイルを作成
            var tileDiv = L.DomUtil.create('div', '');
            tileDiv.setAttribute("style", "border: solid 1px");
            //タイル要素の中にzxyを表示するdiv要素を作成
            var coordsDiv = L.DomUtil.create('div', '', tileDiv);
            coordsDiv.setAttribute("style", "position:absolute;background-color:white;padding:5px;border: solid 1px;left:10px;top:10px;font-size:15px");
            coordsDiv.innerHTML = "z / x / y = " + coords.z + " / " + coords.x + " / " + coords.y;
            return tileDiv;
          },
        });
      </script>
    </head>
    <body onload="init()">
      <div id="mapcontainer" style="position:absolute;top:0;left:0;right:0;bottom:0;"></div>
    </body>
    </html>