🐡

Mapboxのスタイルを体験する

2023/04/12に公開

はじめに

この記事ではMapboxにおけるスタイルの挙動を確認します。具体的にはMapbox GL JSを使って地図を表示し、どのようにスタイルを使うのかを見ていきます。

この記事は以下の企画の子記事です。他サービスの記事へのリンクがあるので、ぜひ合わせてご参照ください。

https://zenn.dev/ottylab/articles/2b0c9d8e918a5a

地図を表示する

サンプル(Display a map on a webpage)を参考にまずは地図を表示してみましょう。

まず、以下のライブラリを読み込みます。

<link href="https://api.mapbox.com/mapbox-gl-js/v2.13.0/mapbox-gl.css" rel="stylesheet">
<script src="https://api.mapbox.com/mapbox-gl-js/v2.13.0/mapbox-gl.js"></script>

CSSを設定します。

<style>
  body { margin: 0; padding: 0; }
  #map { position: absolute; top: 0; bottom: 0; width: 100%; }
</style>

次に地図を表示する場所を作ります。

<div id="map"></div>

JavaScriptのコードは以下のようになります。まず、アクセストークンをmapboxgl.accessTokenに設定します。次にMapクラスのコンストラクタでオプションを設定します。containerで地図を表示するHTML要素、styleで使用するスタイルを指定します。初期位置設定用にcenterzoomが使用できます。

mapboxgl.accessToken = YOUR_API_KEY_HERE;
const map = new mapboxgl.Map({
  container: 'map',
  style: 'mapbox://styles/mapbox/streets-v12',
  center: [-74.5, 40],
  zoom: 9,
});

結果は以下のとおりです。

スタイルとは

Mapboxのスタイルを使いこなす上で欠かせないのがソースとレイヤーの理解です。

ソースは地図の元データになります。例えばMapbox Streets v8というベクタータイルセットには地図の描画に必要なデータが詰め込まれています。データは種類ごとに分類されており、例えばroadというデータには道路や線路のデータが入っています。

レイヤーはソースのデータをどのように描画するかを指定します。例えば road の中には幹線道路、歩道、線路など様々なデータが含まれています。そこで「幹線道路のデータだけに対して色や太さを作用させる幹線道路のレイヤー」のように表現したいものごとにレイヤーを作成していきます。レイヤーは下から上に重ねていきます。最終的に表示される地図は、すべてのレイヤーが重なった状態を上から見ているものとなります。

Mapbox Streets v8が使用されているスタイルとしてMapbox Streets v12が有名です。

ソース + レイヤーをコードで表現してみる

それでは実際にコードで試してみましょう。まず、以下のようにMapオブジェクトを作成する際に指定していたstyleを削除します。

mapboxgl.accessToken = YOUR_API_KEY_HERE;
const map = new mapboxgl.Map({
  container: 'map', 
  center: [-74.5, 40],
  zoom: 9
});

次に、Map#addSourceでソースを登録します。ここではMapbox Streets v8を使用します。第一引数に任意のソースIDを指定し、第二引数にソースの種類やURLを指定します。

map.addSource('streets', {
  type: 'vector',
  url: 'mapbox://mapbox.mapbox-streets-v8'
});

さらに、そのソースを元にレイヤーを作成します。'Map#addLayer'を呼び出します。ここでsourceは先程登録したソースのID、source-layerはソースの中のデータの種類を指定します。paintはどのように表現するかを指定します。typeとしてlineを指定しているので線が描画されますが、その際の色と幅を指定しています。

map.addSource('streets', {
map.addLayer({
  id: 'road',
  type: 'line',
  source: 'streets',
  'source-layer': 'road',
  paint: {
    'line-color': '#00ff00',
    'line-width': 3,
  }
});

表示してみると、以下のように緑色の道路のみの地図が表示されます。非常に単純な例ですが、ソース、レイヤーの仕組みがご理解いただけたかと思います。

ソース + レイヤー = スタイル

さて、JavaScriptでコードを書けばどんな複雑な地図でも表現できることがわかりました。しかし、Mapbox Streets v12のような複雑な地図をすべてJavaScriptのコードで表現するのは非常に手間がかかることが予想されます。また、Mapbox Streets v12をもとに更にカスタマイズを加えていこうと思うと、その都度コードのコピーが必要となり非効率です。

そこで、コードで記述したもとと同じ内容をJSONで宣言的に記述できるようにしたものがスタイルです。以下のように記述すると先ほどと同じ地図が表示されます。

mapboxgl.accessToken = YOUR_API_KEY_HERE;
const map = new mapboxgl.Map({
  container: 'map', 
  center: [-74.5, 40],
  style: {
    "version": 8,
    "name": "Simple Style",
    'sources': {
      streets: {
        type: 'vector',
        url: 'mapbox://mapbox.mapbox-streets-v8'
      }
    },
    "layers": [{
      id: 'road',
      type: 'line',
      source: 'streets',
      'source-layer': 'road',
      paint: {
        'line-color': '#00ff00',
        'line-width': 3,
      }
    }]
  },
  zoom: 9
});

ここでは簡単のために直接JSONをコード内に記述していますが、実用上はJSONファイルとして外部に定義し、style: URLという形で読み込むのが一般的です。最初に地図を表示したときに用いた以下のような記述方法です。

const map = new mapboxgl.Map({
  container: 'map',
  style: 'mapbox://styles/mapbox/streets-v12',
  center: [-74.5, 40],
  zoom: 9
});

このようにソース + レイヤーをスタイルとして定義することで、処理(コード)から地図表現(データ)を分離することができます。これにより、メンテナンス性の向上のみならず、マルチプラットフォームで同じスタイルを使用できる、デザイナーとエンジニアが分業できるなどのメリットが生まれます。ちょうどHTML・JavaScriptとCSSを分離したのと同じような発想です。

Expressions

上記の例では線の色、太さを決め打ちで設定していました。しかし、Expressionsを使うと柔軟な設定ができます。ExpressionsはJSONの配列で表現する式で["op", "param1", ...]という形式を取ります。配列の要素0が命令、それ以降がパラメータです。ちょうどLisp言語のS式のような感じです。

ソースのパラメータのデータを取得する命令、値に応じて処理を変える分岐命令、ズームレベルに応じた線形補間、数学の関数など様々な命令がサポートされています。これにより、値に応じて色を変える、ズームレベルに応じて幅を変えるなど、動的なスタイリングが可能となります。

Mapbox Studioによるスタイルの編集

JSONで記述することでスタイルを作成・編集することができることはわかりました。しかし、複雑な地図のJSONをテキストエディタで編集しつつ結果を確認するのは、やや重労働です。そこでおすすめしたいツールがMapbox Studioです。Webブラウザ上のGUIでスタイルを直感的に編集できるツールで、編集結果はそのままMapboxのサーバにホストされます。つまり、自前でスタイルをホストするサーバを準備する必要なく、mapbox://でアクセス可能なスタイルが作成できます。

Mapbox Studioにアクセスし、「New style」ボタンをクリックすることで新しいスタイルを作成できます。最初にベースとなる地図を選択します。自分でゼロから作りたいときにはBlankを使用しますが、概ねStreetsを編集して使うユースケースが多いでしょう。ここではStreetsをベースに進めます。

編集可能な状態になると3Dの地球が表示されます。Mapbox GL JS v2は地図の3Dマッピング(投影法)がサポートされており、Mapbox Streets v12ではGlobe(3Dの地球)がデフォルトになっています。そのため、ズームレベルの小さい状態ではこのように3Dの地球が表示されます。

Mapbox Studio

さて、今回は高速道路の色を赤色に変えようと思います。東京周辺をズームし、高速道路をクリックします。すると、そのフィーチャー(オブジェクト)がどのレイヤーによって表現されているかがコンテクストメニューで表示されます。

レイヤー情報

road-motorway-trunkをクリックすると、以下のようにそのレイヤーが選択された状態になります。ここで色などを編集します。

レイヤー編集画面

今回は赤色に設定するので以下のようにします。変更は即座に地図上に反映されます。

色変更

編集が完了したら「Publish...」ボタンをクリックしてしProductionモードに変更を反映します。編集中のスタイルはDraftと呼ばれる状態で、開発段階で利用するモードです。そのままでもMapbox GL JS等で使用できますが、レートリミット等の制限が厳しいため、製品版では必ずProductionを使用します。また、DraftをPublishするまでは変更がProductionには反映されないため、一度リリースしたアプリケーションのスタイルを変更する際にも、変更中のスタイルが製品版で表示されてしまうことがありません。開発が完了し、Publishすることで初めて製品版にも反映されます。

スタイルのURLは「Share...」ボタンから確認できます。Style URLのmapbox://で始まるURLをコピーします。

URL

このURLをコードの中で以下のように使用します。

const map = new mapboxgl.Map({
  container: 'map',
  style: 'mapbox://styles/yochi/clgc8zfir000301pdahjtsax8', //ココ
  center: [-74.5, 40],
  zoom: 9
});

このように表示されます。とても簡単ですね!

スタイルを動的に変更

地図を表示した後にスタイルを変更することもできます。Examplesの中のChange a map's styleがわかりやすいので見てみましょう。左上のラジオボタンを選択することでスタイルを変更できます。

スタイルはMap#setStyleを使用することで簡単に変更できます。Exampleでは以下の部分です。

input.onclick = (layer) => {
  const layerId = layer.target.id;
  map.setStyle('mapbox://styles/mapbox/' + layerId);
};

layerId変数にはラジオボタンで選択したsatellite-streets-v12のようなスタイル名が格納されており、このコードでURLが生成されます。そのURLをsetStyleの引数とすることでスタイルが動的に変更されます。

まとめ

Mapboxにおけるスタイルはソースとレイヤーを宣言的に表現したものです。そのため、スタイルの本質はデータの指定とレイヤーの作成です。レイヤーはExpressionsをサポートし、非常に柔軟な表現ができます。また、地図はレイヤーを重ねたものとして表現されるため、必要に応じてレイヤーの重ね順の変更や削除も可能です。これはMapboxが提供しているMapbox Streets v12のようなスタイルにおいても例外ではありません。

つまり、Mapboxを使うことで地図を完全にコントロールすることができます。最初とっつきにくさはあるものの、慣れてしまえばその表現力の高さは手放せないものになると思います。

GitHubで編集を提案
マップボックス・ジャパン合同会社

Discussion