42
Help us understand the problem. What are the problem?

posted at

updated at

Leaflet + OpenStreetMap で地図情報を扱うサンプルコード

Leaflet とは

Leafletは、地図データを扱うためのJavaScript ライブラリ。Google Maps PlatoformやYOLPのようなメソッドで、地図情報の操作ができる。

leaflet_logo.png

Leaflet のウェブサイト
https://leafletjs.com/

Leafletのレポジトリ
https://github.com/Leaflet/Leaflet

Vladimir Agafonkinというエンジニアによって開発された。

著作権は、Vladimir および各コントリビュータに帰属しているが、BSDライセンスを採用しているため、著作権の表示と免責条項さえ明記しておけば、再利用も再配布も自由となる。

Leafletを使えば、すぐに地図が使えるか

No、Leaflet自体は単なるJSライブラリで、地図データを持っていない。

Leaflet で地図を表示するためにはどうすれば良いか

Leallet とは別に、地図サービスからデータを取得する必要がある。

例えば MapBox という有名な地図情報サービスと組み合わせ使うことができる。

この場合、MapBoxにアカウントを作成し、APIキーをあらかじめ取得する必要がある。
利用頻度に応じて、MapBoxに利用料を払う必要がある。

面白い例として、Leaflet にGoogle Maps のデータを組み合わせて表示することも可能なようだ(ただしライセンス違反になる可能性が高い)。

この場合、地図の画像データはGoogle Mapsを使い、描画するためのライブラリにLeafletを利用する形となる。

いずれにせよ、利用する地図データの規約に合わせて、APIキーを取得したり、費用を払う必要がある。

OpenStreetMap とは

OpenStreatMap財団が「Open Data Commons Open Database License (ODbL)」 の下にライセンスするオープンデータ。OpenStreetMapとその協力者をクレジットすれば、データを自由にコピー、配布、送信、利用することができる。料金はかからない。

ライセンスはCreative Commons.
https://www.openstreetmap.org/copyright/ja

Leaflet+ OpenStreetMap 基本的な使い方

公式サイトのチュートリアルが詳しい。

Laefletのライブラリ(CSS、JS)を読み込み、地図データをOpenStreetMapから呼び出して使う。

Leafletを使う場合、必ずCSSを先に呼び出し、その後にJSライブラリを読み込む必要がある。


<link rel="stylesheet" href="https://unpkg.com/leaflet@1.6.0/dist/leaflet.css"
   integrity="sha512-xwE/Az9zrjBIphAcBb3F6JVqxf46+CDLwfLMHloNu6KEQCAWi6HcDUbeOfBIptF7tcCzusKFjFw2yuvEpDL9wQ=="
   crossorigin=""/>
 <!-- Make sure you put this AFTER Leaflet's CSS -->
 <script src="https://unpkg.com/leaflet@1.6.0/dist/leaflet.js"
   integrity="sha512-gZwIG9x3wUXg2hdXF6+rVkLF/0Vi9U8D2Ntg4Ga5I5BZpVkVxlJWbSQtXPSiUTtC0TjtGOmxa1AJPuV0CPthew=="
   crossorigin=""></script>

ライブラリ読み込み後、htmlの body内に、地図表示のためのDOMを設定する。

 <div id="mapid"></div>

地図表示用のDOMサイズを、個別にCSSで指定する。

#mapid { height: 500px; }

DOMの準備ができたら、地図オブジェクトを生成してOpenStreetMapの画像タイルを読み込む。


var map = L.map('mapid', {
  center: [35.66572, 139.73100],
  zoom: 17,
});
var tileLayer = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
  attribution: '© <a href="http://osm.org/copyright">OpenStreetMap</a> contributors, <a href="http://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>',
});
tileLayer.addTo(map);

サンプルコード

地図上にマーカーを表示する

DOMに地図を描画した後、マーカーの情報をオブジェクトに追加する。

// 六本木を中心に地図描画

var map = L.map('mapid', {
  center: [35.66572, 139.73100],
  zoom: 17,
}); 

// OpenStreetMap から地図画像を読み込む

var tileLayer = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
  attribution: '© <a href="http://osm.org/copyright">OpenStreetMap</a> contributors, <a href="http://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>',
});
tileLayer.addTo(map);

// マーカー画像の場所を指定する

L.marker([35.66572, 139.73100]).addTo(map); 

スクリーンショット 2020-05-03 20.00.13.png

動作サンプル

Leaflet+OSM Sample01 マーカーを表示

複数のマーカーを表示する

// 六本木を中心に地図描画

var map = L.map('mapid', {
  center: [35.66572, 139.73100],
  zoom: 17,
}); 

// OpenStreetMap から地図画像を読み込む

var tileLayer = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
  attribution: '© <a href="http://osm.org/copyright">OpenStreetMap</a> contributors, <a href="http://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>',
});
tileLayer.addTo(map);

// マーカー画像の場所を指定する

L.marker([35.66572, 139.73100]).addTo(map); //1つ目のマーカー
L.marker([35.6662186, 139.7303961]).addTo(map); //2つ目のマーカー

スクリーンショット 2020-05-03 20.02.26.png

動作サンプル

Leaflet+OSM Sample02 マーカーを複数表示

geoJson を利用して複数のマーカーを表示する

geoJson は、地理情報を扱うためのJSONフォーマット。
Leafletに用意されている 「geoJson」 メソッドを利用する

// 六本木を中心に地図描画

var map = L.map('mapid', {
  center: [35.66572, 139.73100],
  zoom: 17,
}); 

// OpenStreetMap から地図画像を読み込む

var tileLayer = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
  attribution: '© <a href="http://osm.org/copyright">OpenStreetMap</a> contributors, <a href="http://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>',
});
tileLayer.addTo(map);


var features = [];
var place = [{
  "name": "東京ミッドタウン",
  "lat": "35.66572",
  "long": "139.73100"
}, {
  "name": "サントリー美術館",
  "lat": "35.6662186",
  "long": "139.7303961"
}, ]
// GeoJSON形式で複数個のマーカーを設定する
for (var i = 0; i < place.length; i++) {
  features.push({ // 1つのマーカーの情報を格納する
    "type": "Feature",
    "properties": {
      "name": place[i].name
    },
    "geometry": {
      "type": "Point",
      "coordinates": [place[i].long, place[i].lat]
    }
  });
}
L.geoJson(features).addTo(map);

注意点

geoJson でマーカーを表示する際は、「経度」→「緯度」で位置情報を指定する必要がある。

      "coordinates": [place[i].long, place[i].lat]

「緯度」→「経度」で指定すると、期待通りの描画にならないため、要注意。

動作サンプル

Leaflet+OSM Sample03 geoJson でマーカーを複数表示

マーカーをクリックするとポップアップを表示する

「bindPopup」でイベントをバインドする

var map = L.map('mapid', {
  center: [35.66572, 139.73100],
  zoom: 17,
});
var tileLayer = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
  attribution: '© <a href="http://osm.org/copyright">OpenStreetMap</a> contributors, <a href="http://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>',
});
tileLayer.addTo(map);
var features = [];
var place = [{
  "name": "東京ミッドタウン",
  "lat": "35.66572",
  "long": "139.73100"
}, {
  "name": "サントリー美術館",
  "lat": "35.6662186",
  "long": "139.7303961"
}, ]
// GeoJSON形式で複数個のマーカーを設定する
for (var i = 0; i < place.length; i++) {
  features.push({ 
    "type": "Feature",
    "properties": {
      "name": place[i].name
    },
    "geometry": {
      "type": "Point",
      "coordinates": [place[i].long, place[i].lat]
    }
  });
}
L.geoJson(features, {
  onEachFeature: function(features, layer) {
    if (features.properties && features.properties.name) {
      layer.bindPopup(features.properties.name);
    }
  }
}).addTo(map);

スクリーンショット 2020-05-03 20.09.22.png

動作サンプル

Leaflet+OSM Sample04 マーカーをクリックするとポップアップが表示される

マーカーに MouseOver、Click イベントを仕掛ける

先にbindPopup をバインドし、MouseOver, click イベントを重ねる。

  • マーカーの上にマウスを重ねると、ポップアップが表示
  • マーカーをクリックすると、アラートが表示される

というユースケースで書いてみた。

var map = L.map('mapid', {
  center: [35.66572, 139.73100],
  zoom: 17,
});
var tileLayer = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
  attribution: '© <a href="http://osm.org/copyright">OpenStreetMap</a> contributors, <a href="http://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>',
});
tileLayer.addTo(map);
var features = [];
var place = [{
  "name": "東京ミッドタウン",
  "lat": "35.66572",
  "long": "139.73100"
}, {
  "name": "サントリー美術館",
  "lat": "35.6662186",
  "long": "139.7303961"
}, ]
// GeoJSON形式で複数個のマーカーを設定する
for (var i = 0; i < place.length; i++) {
  features.push({ 
    "type": "Feature",
    "properties": {
      "name": place[i].name
    },
    "geometry": {
      "type": "Point",
      "coordinates": [place[i].long, place[i].lat]
    }
  });
}
L.geoJson(features, {
  onEachFeature: function(features, layer) {
    if (features.properties && features.properties.name) {
      layer.bindPopup(features.properties.name);
      layer.on('mouseover', function(e) {
        this.openPopup();
      });
      layer.on('mouseout', function(e) {
        this.closePopup();
      });
      layer.on('click', function(e) {
        alert('Yay!');
      });
    }
  }
}).addTo(map);

スクリーンショット 2020-05-03 20.17.25.png

動作サンプル

Leaflet+OSM Sample05 マーカーにonMouse、onClickそれぞれにイベントを仕掛ける

地図の中央に十字マークを表示する

「あちあち情報局」様のコードを参考にしました。
十字マークは、国土地理院より拝借。


// 十字マークを地図中央に表示

var crossIcon = L.icon({
  iconUrl: 'https://maps.gsi.go.jp/image/map/crosshairs.png',
  iconSize: [32, 32], 
  iconAnchor: [16, 16] 
});

var crossMarker = L.marker( map.getCenter(),{
  icon:crossIcon,  
  zIndexOffset:1000, 
  interactive:false 
}).addTo(map);


map.on('move', function(e) {
  crossMarker.setLatLng(map.getCenter());
});


スクリーンショット 2020-05-04 18.37.46.png

動作サンプル

Leaflet+OSM Sample06 地図の中央に十字マークを表示する

現在地の情報を取得して地図を表示する

「locate」 メソッドを使う。geoLocation のラッパーのようで、ブラウザが geoLocation API 対応なら、現在地を取得して処理を行ってくれる。

locateメソッドとは別に、onLocationFound, onLocationError という2つのメソッドがある。

それぞれ、現在位置の取得の成功・失敗に応じてイベントの発火ができる。
より細かい処理が必要な場合に有用。

// 現在地を取得して、地図を描画する

function getgeo() {
  map.on('locationerror', onLocationError);
  map.locate({
    setView: "true"
  });
}

function onLocationError(e) {
  alert(e.message);
}

動作サンプル

Leaflet+OSM Sample07 現在地の地図を表示する

地図の縮尺について

PCの画面で locateメソッドを使うと、地図の描写がZoomOutされ、細かい地図の描写ができない。
その一方、スマートフォンで locate 機能を使うと、直前まで表示していた地図の縮尺レベルを保ったまま、現在地周辺の地図を描画する。

スマホで使う分には問題ないが、ちょっと気持ち悪い。どうすれば解決するか、調査中。

地図をドラッグ移動後、中心点の緯度経度を表示する

「moveend」 メソッドを使う。getCenter と組み合わせて、

  • 地図の移動終了を、moveend で検知し、イベント発火
  • getCenter で中心地の緯度経度を取得する

という流れで処理する。

  • 緯度は getCenter().lat
  • 経度は getCenter().lng

で取得できる。

map.on('moveend', function(e) {
  console.log("緯度: " + map.getCenter().lat);
  console.log("経度: " + map.getCenter().lng);   
});

スクリーンショット 2020-05-05 12.03.17.png

動作サンプル

Leaflet+OSM Sample08 地図をドラッグ移動後、中心の緯度経度を表示

コントローラーの表示位置を調整する

を使うと楽。

LeafletにはDOM操作用のメソッドがいくつか用意されており、それを使う。

「右上」「右下」「左上」「左下」 の指定ができる。


map.zoomControl.setPosition("bottomleft");

StackOverflow をみていると、別なやり方も見つけられるが、上記の方がシンプル。

スクリーンショット 2020-05-05 12.45.33.png

動作サンプル

Leaflet+OSM Sample09 Zoomコントローラーの位置を変える

実稼働例

個人で開発・運営しているウェブサービスに、Leaflet と OpenStreetMap で地図表示を行いました。こちらに反映してます。

tenkinoseichi_main.jpg

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Sign upLogin
42
Help us understand the problem. What are the problem?