L.map
で作成するマップオブジェクトには、
レイヤ管理機能がある。レイヤは何枚でも重ねられるが以下の2つに分類される。
表示地図の基盤となるレイヤで、複数のレイヤのうち 1枚だけ表示させることができる。
ベースレイヤの上に重ねて表示するレイヤで 同時に何枚でも表示させることができる。
ベースレイヤはタイル地図を保持するレイヤ、 オーバーレイレイヤには地点の註釈となるマーカや 経路を示すラインオブジェクト、エリアを表示するポリゴンなどを 格納するレイヤを登録するのが主な使い方となる。
Leaflet
では2種類のレイヤをまとめて管理するコントロールレイヤを利用する。
L.control.layers()
を以下のように使用する。
L.control.layers(BaseConf[, OvConf[, Options]])
ベースレイヤをJSON形式で指定する。
{'レイヤ名1': layer1, 'レイヤ名2': layer2, ...}
のような並びであり、 キーに指定した部分がレイヤ選択メニューに現れる。HTML の要素を表す文字列を渡して画像などを選択リストに出すこともできる。
オーバーレイレイヤをJSON形式で指定する。 指定の書式はベースレイヤ同様。
コントロールレイヤのオプションを指定する。主なオプション 2つについて示す。
position | レイヤ制御アイコンのマップ内での配置位置。 'topright'(デフォルト), 'topleft', 'bottomleft', 'bottomright' のいずれか。 |
collapsed | レイヤ制御アイコンはマウスポインタを合わせると レイヤ一覧が出るが普段は畳まれている。 この挙動(true)をやめて畳まなくする場合は false を設定する。 |
例として、以下のような4つのレイヤを持つマップの作成を示す。
(1)OpenStreetMapタイルレイヤ
(2)国土地理院タイルレイヤ
特定地点のマーカを2つ含むレイヤ
document.addEventListener("DOMContentLoaded", () => { // 例: コントロールレイヤ var mlCenter = [38.891, 139.824]; var otherPoint = [38.890, 139.822]; var osmTile = // OSMのタイル L.tileLayer('//{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { attribution: '© <a href="http://osm.org/copyright">OpenStreetMap</a> \ contributors' }); var gsiTile = // 国土地理院タイル L.tileLayer('//cyberjapandata.gsi.go.jp/xyz/std/{z}/{x}/{y}.png', { attribution: '<a href="http://maps.gsi.go.jp/development/ichiran.html">国土地理院</a>' }); var markerLayer1 = L.marker(mlCenter); var markerLayer2 = L.marker(otherPoint); var mlmap = L.map("markerlayer", { layers: [gsiTile, markerLayer1, markerLayer2], center: mlCenter, zoom: 16, scrollWheelZoom: false }); /* 上記4行は以下のようにするのと同じ効果 var mlmap = L.map("markerlayer").setView(mlCenter, 16); gsiTile.addTo(mlmap); markerLayer1.addTo(mlmap); markerLayer2.addTo(mlmap); */ // 以下、コントロールレイヤの設定 // ベースレイヤはOSMと国土地理院2つをJSON形式で指定 var baseLayers = {'OpenStreetMap': osmTile, '国土地理院': gsiTile}; // マーカーを2つオーバーレイレイヤに指定 var ovlLayers = {'マーク1': markerLayer1, 'マーク2': markerLayer2}; // ベースレイヤとオーバーレイレイヤをマップに追加 L.control.layers(baseLayers, ovlLayers).addTo(mlmap); }, false); // HTML文書がロードされたら上のアロー関数を呼ばせる
<!DOCTYPE html> <html lang="ja"> <head> <title>MarkerLayer</title> <link rel="stylesheet" type="text/css" href="../src/leaflet.css"> <script type="text/javascript" src="../src/leaflet.js"></script> <script type="text/javascript" src="markerlayer.js"></script> <style type="text/css"> <!-- div#markerlayer {width: 90vw; height: 90vw; margin: auto;} --> </style> </head> <body> <div id="markerlayer"></div> </body> </html>
→ 実例
マーカやポリラインなどのオブジェクトは1つ生成すると 1枚のレイヤとして返されマップに貼り付けることになる。 複数のオブジェクトをまとめて1枚のレイヤで管理したいときには レイヤグループ を使用する。
上記の例 markerlayer.js
において、2つのマーカオブジェクト(レイヤ)をまとめて
1つにしてマップ上に配置するように変えるには、
L.layerGroup
に複数のレイヤを含む配列を渡しグループ化し、
それをマップに貼り付けるとよい。具体的には以下のようにする。
var markerLayer1 = L.marker(mlCenter); // マーカレイヤその1 var markerLayer2 = L.marker(otherPoint); // マーカレイヤその2 // 2つのレイヤをまとめる var markerLayerGroup = L.layerGroup([markerLayer1, markerLayer2]); // まとめたレイヤをマップに貼り付ける var mlmap = L.map("markerlayergroup", { layers: [gsiTile, markerLayerGroup], center: mlCenter, zoom: 16, scrollWheelZoom: false });
マーカ、ライン、ポリゴンなどを地図上に配置したものを表現する形式に GeoJSON がある。たとえば以下のような値である。
var triangle = /* この行は便宜上追加。GeoJSON値は次の行から */ { "type": "FeatureCollection", "features": [ { "type": "Feature", "properties": { "_storage_options": { "color": "Aqua", "opacity": "0.8" } }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 139.82367396354678, 38.892920059048464 ], [ 139.82132434844974, 38.89035641677137 ], [ 139.8262166976929, 38.89023950579702 ], [ 139.82367396354678, 38.892920059048464 ] ] ] } } ] }
これは地図上に配置する Aqua 色のポリゴンを示す値である。
変数 triangle
に代入したこの値を GeoJSON
レイヤに変換してマップに貼り付けることができる。変換には
L.geoJson()
を用いる。
L.geoJson([GeoJSON[, Option]])
GeoJSON にはGeoJSONの値を、Option には GeoJSON 値の処理時に必要なオプション、 あるいはポリゴン等オブジェクトの描画に関るオプションを指定できる。 GeoJSON 値の処理に関るオプション一覧を以下に示す。
オプション | 働き |
---|---|
pointToLayer |
ポイントデータをマップ上に独自オブジェクトとして配置したい場合に用いる。 オブジェクトと座標を引数として受け取り、 その座標に配置すべきオブジェクトの値を返す関数を指定する。 ★★★ office.html |
style |
オブジェクトを1つずつ受け取り、
そのスタイルオプションをJSON形式で返す関数を指定する。たとえば
style: function(feature) { return {color: "red"}; } {color: "red"}
を指定したのと同じことになる。
|
onEachFeature |
オブジェクト、レイヤを1つずつ受け取り、
繰り返し実行する関数を指定する。たとえば、GeoJSON
形式で打ち込んだポイントに "message"
プロパティが設定してあるようなデータを読み取る場合には、
onEachFeature: function(feature, layer) { if (feature.properties && feature.properties.message) { layer.bindPopup(feature.properties.message); } } |
filter |
引数に対象オブジェクトを受け取り、それを表示すべきか否かをブール値で 返す関数を指定する。false を返す場合はレイヤに載せない。 |
上記の GeoJSON 値代入式を読み込み、
マップ上のレイヤとして反映させる例を示す。
HTML文書ではあらかじめ triangle.geojson
をロードしておく。
<script type="text/javascript" src="triangle.geojson" charset="utf-8"> </script>
その部分より後ろで、変数 triangle
に代入された GeoJSON 値を GeoJSON レイヤに貼り付けるコードを記述する。
例示簡略化のため HTML 文書内に JavaScript コードも書く形式で示す。
<div id="geojson" style="width: 80vw; height: 80vh;"></div>
var jsonmap = L.map("jsonmap").setView([38.891, 139.824], 16);
L.tileLayer(
'//{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
{attribution:
'© <a href="http://osm.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
}).addTo(jsonmap);
L.geoJson(triangle, { // triangle変数は triangle.geojson からロードした
style: function (feature) {return feature.properties;}
}).addTo(jsonmap);
このようにすることで、正しい GeoJSON 形式で保存された地理的データをマップ上に配置することができる。
なお、GeoJSON オブジェクトにポップアップを含む場合は、
ポップアップ時に出現させたい文字列をオブジェクトの bindPopup()
メソッドで登録する。このためには、元となる GeoJSON
値にどのようにポップアップ文字列を埋め込んでおくかの取り決めが必要となる。
uMapで作成した地図とオブジェクトの例
たとえば、マップ上の任意の位置に自由にマーカやラインなどを配置でき、 それを GeoJSON 形式で保存できるフリーなサービスである uMap では、 水色に色付けしたポリゴンとそれに設定したポップアップ文字列は以下のように GeoJSON 化される。例として、ポリゴンによる三角形とポップアップマーカを 1つずつ配置した地図を用意した。
この2つのオブジェクトいずれにもポップアップ表示を付加してある。 そのGeoJSONリストを以下に示す。
uMapで生成されたGeoJSONファイル
var triangleUmap = // この1行だけ手で追加した。 { "type": "FeatureCollection", "features": [ {"type": "Feature", // (1)Feature その1 "properties": { "name": "三角地帯", "description": "飯森山の三角形" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 139.8236417770386, 38.89282820375738 ], [ 139.82629179954532, 38.89027290895218 ], [ 139.8214423656464, 38.89045662602478 ], [ 139.8236417770386, 38.89282820375738 ]]]}},{ // (2)Feature その2 "type": "Feature", "properties": { "name": "南洲神社", "description": "西郷隆盛像あり" }, "geometry": { "type": "Point", "coordinates": [ 139.82450008392337, 38.88983866670988 ]}}] }
このGeoJSON情報を Leaflet
のマップ上にポップアップつきで貼り付けることを考える。
GeoJSONで表現されたオブジェクト(群)を上述の L.geoJson
関数で変換する際のオプション(Option)の onEachFeature
を利用する。リスト「uMapで生成されたGeoJSONファイル」中、四角枠で囲った2つがそれぞれ
Feature にあたり、これらが onEachFeature
に指定した関数の第一引数に与えられる。
これを踏まえると次のような GeoJSON
生成でポップアップ登録ができる。
onEachFeature: function(j, layer) {
let p = j.properties;
if (p) {
let name = p.name, desc = p.description;
let popup = "<h3>" + name + "</h3>" + "<p>" + desc + "</p>";
layer.bindPopup(popup);
}
}
各 Feature を仮引数 j
で受けると、properties
キーの持つJSONの、さらに内部のプロパティ
name
と description
が名前と説明文に当たるため、これを h2 要素と p
要素に仕立てたものをポップアップ文字列としている。
以上をまとめたものの、HTML ファイル(triangle-umap.html)と JavaScript プログラム(triangle-umap.js)を以下に示す。
triangle-umap.html
<!DOCTYPE html> <html lang="ja"> <head> <title>uMap GeoJSON</title> <meta name="viewport" content="width=device-width, initial-scale=1" /> <link rel="stylesheet" href="../src/leaflet.css" /> <script src="../src/leaflet.js"></script> <style type="text/css"> <!-- div#umap-json {width: 90vw; height: 80vh; margin: 0 auto;} --> </style> </head> <body> <h1>Triangle GeoJSON by uMap</h1> <div id="umap-json"></div> <script type="text/javascript" src="../src/triangle-umap.geojson" charset="utf-8"></script> <script type="text/javascript" src="../src/triangle-umap.js" charset="utf-8"></script> </body> </html>
triangle-umap.js
// 例: ポップアップつきのuMapオブジェクトを再現する var jsonmap = L.map("umap-json").setView([38.891, 139.824], 16); var layer = L.tileLayer( 'https://cyberjapandata.gsi.go.jp/xyz/std/{z}/{x}/{y}.png', {attribution: '© <a href="//www.gsi.go.jp/kikakuchousei/kikakuchousei40182.html">国土地理院</a>' }).addTo(jsonmap); var gj = L.geoJson(triangleUmap, { style: function (feature) { return feature.properties; }, onEachFeature: function(j, layer) { let p = j.properties; if (p) { let name = p.name, desc = p.description; let popup = "<h3>" + name + "</h3>" + "<p>" + desc + "</p>"; layer.bindPopup(popup); } } }); // L.control.layers(null, {"Triangle": gj}).addTo(jsonmap); gj.addTo(jsonmap)
triangle-umap.html では、オブジェクトを含む GeoJSON データファイルに手を加え、 変数に GeoJSON 値を代入する JavaScript プログラムとしてから HTML ファイルで読み込む細工をした。この方式では、uMap 等でマップデータを編集・保存する度にファイルに手を加える必要があり効率的でない。 GeoJSON ファイルを純粋にデータとして、JavaScript プログラムからロードする方法に変えることにより余計な手間をなくせる。
JavaScript プログラムから外部ファイル中にある JavaScript オブジェクトを読み取る方法はいくつかある。ここでは leaflet.js と組み合わせる事を前提として作られた leaflet-omnivore ライブラリを利用する例を示す。
https://github.com/mapbox/leaflet-omnivore/
を開く。
leaflet-omnivore.js
と
leaflet-omnivore.min.js
が見える。もし、一覧になければ「Tag」プルダウンメニューを開き
Tags 一覧から最新版のひとつ前(例としてv0.3.3)を選ぶと出てくる。
どちらを利用してもよいがここでは圧縮版の
leaflet-omnivore.min.js
を利用する。ファイル名をクリックすると
内容が見えるので [Raw] ボタンを押し、得られた内容をブラウザの機能で保存する。
これを、leaflet.js
と同じディレクトリに配置する。
先ほど作成した
triangle-umap.html
、triangle-umap.js
の組み合わせでは、HTML 側で
<script type="text/javascript" src="../src/triangle-umap.geojson" charset="utf-8"></script>
とし、プログラム化したファイルをロードして GeoJSON
値をグローバル変数 triangleUmap
に代入させ、js
ファイル側でその変数を直接利用していた。leaflet-omnivore
を利用する場合は、HTML 側で leaflet-omnivore.min.js
を読み込むだけにして、プログラムで GeoJSON
値のみを記したファイルをロードする形式に改める。
<!DOCTYPE html> <html lang="ja"> <head> <title>uMap GeoJSON</title> <meta name="viewport" content="width=device-width, initial-scale=1" /> <link rel="stylesheet" href="../src/leaflet.css" /> <script src="../src/leaflet.js"></script> <script src="../src/leaflet-omnivore.min.js"></script> <style type="text/css"> <!-- div#load-json {width: 90vw; height: 80vh; margin: 0 auto;} --> </style> </head> <body> <h1>Triangle GeoJSON by uMap</h1> <div id="umap-json"></div> <script type="text/javascript" src="triangle-load.js" charset="utf-8"></script> </body> </html>
(() => { // 例: GeoJSONファイルを leaflet-omnivore でロードする var jsonmap = L.map("load-json").setView([38.891, 139.824], 16); var layer = L.tileLayer( 'https://cyberjapandata.gsi.go.jp/xyz/std/{z}/{x}/{y}.png', {attribution: '© <a href="//www.gsi.go.jp/kikakuchousei/kikakuchousei40182.html">国土地理院</a>' }).addTo(jsonmap); var customLayer = L.geoJson(null, { // omnivoreに引き渡すGeoJSONレイヤ onEachFeature: function(f, layer) { // このブロックは let p = f.properties; // triangle-umap.js と同じ if (p) { // let name = p.name, desc = p.description; let popup = "<h3>" + name + "</h3>" + "<p>" + (desc||"") + "</p>"; layer.bindPopup(popup); // } } }); // geojson外部ファイルの読み込みは次の行 var gjl = omnivore.geojson("triangle-load.geojson", null, customLayer); // ↑引数は順に: ファイル, 解析オプション, カスタムレイヤ gjl.on("ready", function() { // 'ready' イベントに読み終わったときの処理 jsonmap.fitBounds(gjl.getBounds()); // 読み取り失敗時は 'error' イベント }); gjl.addTo(jsonmap); // マップに足す L.control.layers(null, {"Triangle": gjl}).addTo(jsonmap); })();
leaflet-omnivore では、ファイルからロードした GeoJSON
レイヤのオブジェクトがファイルからの値読み込みを完了したときに
ready
イベントを発行する。これを捕捉する関数を登録しておくことで、
マップオブジェクトが表示の中心となるような処理が可能となる。
これを行なっているのが gjl.on('ready', ...)
の部分である。
→ triangle-load.geojson, triangle-load.html
leaflet-omnivore を利用すると GeoJSON 以外の主要な地理データファイルも読み込むことができる。
たとえばGPSロガーでよく用いられる形式である GPX、 Google Mapで利用されている KML ファイルはそれぞれ以下のようにLeafletレイヤに取り込める。
omnivore.gpx(GPXファイル).addTo(map); omnivore.kml(KMLファイル).addTo(map);
GeoJSON, KML, GPX 3種のファイルを同一マップ上にレイヤ表示する 例を示す。
以下の手順により、マップ上に存在するいくつかのオブジェクトを GeoJSON ファイルとして作成し、それらを表示する Web ページを作成せよ。大まかな手順は以下のとおりである。
leaflet-omnivore-template.html
を利用してよい。leaflet-omnivore 利用のテンプレート leaflet-omnivore-template.html
<!DOCTYPE html> <html lang="ja"> <head><title>Loading GeoJSON with leaflet-omnivore</title> <meta name="viewport" content="width=device-width, initial-scale=1" /> <link rel="stylesheet" href="../src/leaflet.css" /> <script src="../src/leaflet.js"></script> <script src="../src/leaflet-omnivore.min.js"></script> <style type="text/css"> <!-- div#map {width: 90vw; height: 80vh; margin: 0 auto;} --> </style> </head> <body> <h1>例:GeoJSON ロード</h1> <div id="map"></div> <script type="text/javascript" src="[[ここをjsプログラムに変える]].js" charset="utf-8"></script> </body> </html>