📈

Mapbox Newsletter WEEKLY TIPSの解説 -「ズーム・レベルに応じて建物の色を変更」

2024/06/24に公開

はじめに

この記事は、先日配信されたMapbox NewsletterのWEEKLY TIPSで紹介されていた「ズーム・レベルに応じて建物の色を変更」についての解説です。このサンプルではMap#setPaintPropertyExpressionsの使い方について例示しています。また、Newsletterの購読はこちらからお申し込みいただけます。

以下が本サンプルのデモです。

コードを確認

まずExamplesのコードを見に行きましょう。

日本語サイト

英語サイト

基本的に同じコードですが、英語版はスタイルがMapbox Streets v12にアップグレードされているのでこちらを使用します。Mapbox Streets v11ではデフォルトのプロジェクションがWebメルカトルであるのに対し、Mapbox Streets v12ではGlobe(3D表示された地球)なので、印象がかなり異なります。また、英語版はMapbox GL JS v3が使用されています。

HTML/CSS

まずHTMLを見ていきましょう。

以下は地図を表示するエレメントです。

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

以下は地図上部にある「Zoom to buildings」ボタンです。

<button id="zoom">Zoom to buildings</button>

また、このボタンのスタイルが以下のように定義されています。

#zoom {
  display: block;
  position: relative;
  margin: 20px auto;
  width: 50%;
  height: 40px;
  padding: 10px;
  border: none;
  border-radius: 3px;
  font-size: 12px;
  text-align: center;
  color: #fff;
  background: #ee8a65;
}

Mapの作成

次にJavaScriptのコードを見ていきます。以下のコードはいつも通り、Mapオブジェクトを作成しています。containerで地図を表示するHTMLエレメントのidを指定します。

const map = new mapboxgl.Map({
  container: 'map', // container ID
  // Choose from Mapbox's core styles, or make your own style with Mapbox Studio
  style: 'mapbox://styles/mapbox/streets-v12', // style URL
  center: [-90.73414, 14.55524], // starting position [lng, lat]
  zoom: 15 // starting zoom
});

プロパティの設定

建物の色を変えるために、Streets v12スタイルのbuildingレイヤーのプロパティを設定します。これはスタイルが読み込まれた後に設定する必要があるため、map.on('load', () => { /* ここ */})の部分に処理を書きます。

プロパティの値の設定にはMap#setPaintPropertyを使用します。第1引数がレイヤーID、第2引数がプロパティ名、第3引数が設定する値になります。値の部分はExpressionsが記述できます。

まず、このサンプルで使用されているExpressionsであるinterpolateについて説明します。このExpressionsは補間の方法を指定します。例えばzoom 16で赤色、zoom 20で青色とした場合、途中のズームレベルではこの2点の間の色になるように適切に補間させることができます (線形補間のとき、zoom 18で紫色)。第1引数は補間の種類をlinearexponentialcubic-bezierから選択します。第2引数はinputとして使用するプロパティ等(先程の例ではズームレベル)を指定します。これ以降の引数は「inputの値とその時の出力値」の順番で並べていきます。

それではコードを確認します。まず1つ目のプロパティはfill-colorです。これはfillレイヤー(ポリゴン)の色を設定するプロパティです。補間方法はexponentialを指定しているので、指数関数的な変化となります。底として0.5を指定しています。inputはzoom、色はzoom 15のときベージュ、zoom 18で黄色になります。

map.setPaintProperty('building', 'fill-color', [
  'interpolate',
  // Set the exponential rate of change to 0.5
  ['exponential', 0.5],
  ['zoom'],
  // When zoom is 15, buildings will be beige.
  15,
  '#D9D3C9',
  // When zoom is 18 or higher, buildings will be yellow.
  18,
  '#ffd700'
]);

2つ目のプロパティはfill-opacityです。これはfillレイヤー (ポリゴン) の不透明度を設定するプロパティです。補間方法はexponentialを指定しているので、指数関数的な変化となります。inputはzoom、不透明度はzoom 10のとき0.5、zoom 18で1になります。

map.setPaintProperty('building', 'fill-opacity', [
  'interpolate',
  // Set the exponential rate of change to 0.5
  ['exponential', 0.5],
  ['zoom'],
  // When zoom is 10, buildings will be 100% transparent.
  10,
  0.5,
  // When zoom is 18 or higher, buildings will be 100% opaque.
  18,
  1
]);

ボタンの処理

ボタンクリック時の処理です。具体的にはMap#zoomToでズームレベルを18にしています。また、オプションとして9秒かけてズームする設定になっています。

document.getElementById('zoom').addEventListener('click', () => {
    map.zoomTo(18, { duration: 9000 });
});

まとめ

setPaintPropertyとExpressionsを使用することで、動的に見た目を変更できることがわかりました。

おまけ - Exponential

interpolateの第1引数をexponentialとすると、指数関数的な補間が行われます。ここでは実際にどのような曲線が描かれるのか、また底の値でどのような違いが生じるのかを見てみます。

まず、exponental選択時に行われる内部での計算ロジックについてはMapbox GL JSのソースコード内にコメントがあります。

https://github.com/mapbox/mapbox-gl-js/blob/v3.4.0/src/style-spec/expression/definitions/interpolate.js#L220-L254

exponentialの曲線の概念を表現しているのは以下の式です。

f(x) = a * base^x + b

xが入力(サンプルではズーム)、f(x)が出力(サンプルでは色)、baseが底です。abは入出力の上限・下限から決まる定数です。つまり、exponentialという名前の通り、指数関数的な曲線が描かれます。

具体的に見てみます。

単調増加

xが増加したときにyも増加するケース。底は0.5, 0.75, 1, 1.25, 1.5。底によって曲線の膨らみ具合が変化していく様子がわかります。

exp1

単調減少

xが増加したときにyが減少するケース。底は0.5, 0.75, 1, 1.25, 1.5。このケースではaが負になるので曲線の膨らみ具合が逆転します。

exp2

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

Discussion