Mobile Factory Tech Blog

技術好きな方へ!モバイルファクトリーのエンジニアたちが楽しい技術話をお届けします!

Mapbox GL JS を使って ブラウザで動作可能な地図を作る

はじめに

この記事は モバイルファクトリー Advent Calendar 2019の11日目の記事です.

こんにちは,今年度よりモバイルファクトリーに入社した yasuda です

今回は,ブラウザ上で地図を描画するライブラリである MapboxGL JS と,その使用例について紹介します

前提

この記事は,以下のバージョンを元に執筆しています

  • mapbox-gl-js: 1.6.0

また,本記事内で用いられる座標(緯度・軽度)は,全てGoogle Maps などから目視で取得したデータです

Mapbox GL JS について

Mapbox GL JS は,WebGL で動作する地図ライブラリです

Webでの動作のため,PCでもモバイル端末でも扱うことができます

また,地図配信サービスの Mapbox と組み合わせて,オリジナルの地図を配信・表示することができます

最小構成

まずは,mapbox-gl を使って最低限の設定をして地図を表示してみます

ここでは,公式HPのサンプルコードを参考にします

今回は,地図のスタイルデータについては既にできているものとして,詳しくは扱いません

<body>
  <div id='map'></div>
</body>

htmlは,id: map のタグ1つのみ用意します

body {
  margin: 0;
  padding: 0;
}

#map {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 100%;
}

css では,全画面表示を行うためのスタイルを当てます

mapboxgl.accessToken = 'XXXX'
style = 'YYYY.json'

window.onload = () => {
  var map = new mapboxgl.Map({
    container: 'map',
    style: style,
  })
}

jsで,mapboxgl の初期化処理を行います (accessToken と style は適宜変えてください)

これだけで,地図の描画ができます

ドラッグで移動,ダブルクリックやホイールの回転でズームといった基本的な操作も行うことができます

また,右クリックでドラッグすると,地図を回転させることもできます

そして,Mapbox GL JS はモバイル端末でも使用でき,スワイプやピンチイン・アウトといった操作も可能です

では,ここからは Mapbox GL JS の使用例を,実際に使いながら紹介していきます

初期座標を変える

これも初めの例に乗っていたのですが, mapboxgl の初期化時に center(緯度・経度) と zoom を渡すことで,初期値を変更することができます

今回は,適当に五反田駅辺りを中央にしてみました

center: [ 139.7237285, 35.6262489 ],
zoom: 15,

例えば,使用している端末の位置情報をこの初期値に渡すことで,使用者の周辺の地図を映すといったことができます

マーカを使用する

Mapbox GL JS には,マーカ機能があり,地図が元々持っている情報とは別に,追加で目印をつけることができます

let marker = new mapboxgl.Marker()
  .setLngLat([ 139.727101, 35.6259434 ])
  .addTo(map)

例として,弊社オフィスの近くにマーカをおいてみます

右の方にある,青いマーカが弊社の場所です

ここはさっきの五反田駅を中心に移した地図にも写っていたのですが,このようにマーカを使うことでより目立たせることができます

今回使用したのはデフォルトのマーカですが,htmlとstyleを定義することで,オリジナルのマーカを使用することもできます

マーカを複数配置する場合

複数のマーカを同時に配置する場合マーカの描画順序に注意する必要があります

例えば,2つのマーカが前後に隣接している場合,画面上で上に位置するマーカは, 後方 にあると認知します

こちらは,南北に真っ直ぐ進む京都市営地下鉄烏丸線の各駅を,駅メモ!の地図で見た画像です

このアイコンは,影が付いていることからも前後関係を重要視していることがわかります.

しかし,Mapbox GL JS には複数のマーカの描画順序を設定する機能は無く,デフォルトでは .addTo(map) によって登録した順に描画されます

その際に前後関係が正しくなるように登録すれば良いと思うのですが, 地図が回転すると初期の前後関係では破綻することがあります

そこで,地図が回転すると動的にマーカの前後関係を修復する処理を追加してみます

this.map.on('rotate', () => {
  var markerElements
  if (markerElements || markerElements.length) {
    markerElements = Array.from(document.getElementsByClassName('mapboxgl-marker'))
  }
  let yPositions = []
  markerElements.forEach((marker) => {
    let transform = marker.style.transform
    yPositions.push(parseInt(transform.slice(transform.lastIndexOf(',') + 1)))
  })
  let minY
  if (yPositions && yPositions.length) {
    minY = yPositions.reduce( (a, b) => a < b ? a : b)
  }
 markerElements.forEach((marker, index) => {
    marker.style.zIndex = yPositions[index] - minY + 1
  })
})

地図が回転した際に呼ばれるコールバック関数を登録し,全マーカのhtml要素から transformのy要素でソートし, z-index に渡します (この方法では,地図以外の要素のz-indexとの干渉を考慮していないので,必要に応じて変えてください)

地図上に線を引く

先程は,特定の地点を目立たせるマーカを配置しましたが,次は地図上に線を描画して,道のりを目立たせてみます

Mapbox GL JS は GeoJSON を扱うことができ,座標などを含むjsonを渡すことで,線や面の描画ができます

  map.on('load', function(){
    const lngLats = [
      [ 139.7029507, 35.657829 ],
      [ 139.7107544, 35.6462484 ],
      [ 139.7162516, 35.6343683 ],
      [ 139.7237285, 35.6262489 ],
      [ 139.7283931, 35.6198556 ],
      [ 139.7388572, 35.6299566 ],
    ]
    map.addLayer({
      'id': 'route',
      'type': 'line',
      'source': {
        'type': 'geojson',
        'data': {
          'type': 'Feature',
          'geometry': {
            'type': 'LineString',
            'coordinates': lngLats,
          }
        }
      },
      'paint': {
        'line-color': '#0c7',
        'line-width': 8,
      }
    })
  })

例として,山手線の渋谷-品川間の各駅を結ぶ折れ線を引きました

他にも,ユーザが定期的に現在地を登録していれば,その日に移動した経路を地図上に表示したりすることもできます

注意点として, geoJSONを地図に描画する map.addLayer() は, mapboxgl の初期化処理が終了した後に呼び出す必要があります

地図の初期化処理である new mapboxgl.Map() の完了には少し時間がかかるため, map.on('load', function(){~}) などで読み込みが完了したのを待ってからgeoJSONの追加処理を行うと安全です

終わりに

Mapbox GL JS によって,モバイルのネイティブアプリとも大差ない機能を持つ地図を作ることができます.

本記事では,その使用例の一部を紹介しました.

Mapbox GL JS には今回紹介しきれていない多くの機能が備わっており,様々な地図アプリを作ることができます