Mapbox Newsletter WEEKLY TIPSの解説 -「マップの場所をスライドショーとして再生」
はじめに
この記事は、先日配信されたMapbox NewsletterのWEEKLY TIPSで紹介されていた「マップの場所をスライドショーとして再生」についての解説です。このサンプルではflytTo
やmoveend
の使い方を紹介しています。また、Newsletterの購読はこちらからお申し込みいただけます。
コードを確認
まずExamplesのコードを見に行きましょう。
日本語サイト
英語サイト
基本的に同じコードですが、英語版はスタイルがMapbox Streets v12にアップグレードされているのでこちらを使用します。Mapbox Streets v11ではデフォルトのプロジェクションがWebメルカトルであるのに対し、Mapbox Streets v12ではGlobe(3D表示された地球)なので、印象がかなり異なります。
HTML/CSS
CSSとしては以下のスタイルを定義しています。
.map-overlay-container {
position: absolute;
width: 25%;
top: 0;
left: 0;
padding: 10px;
z-index: 1;
}
.map-overlay {
font: 12px/20px 'Helvetica Neue', Arial, Helvetica, sans-serif;
background-color: #fff;
border-radius: 3px;
padding: 10px;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
}
.map-overlay h2,
.map-overlay p {
margin: 0 0 10px;
}
このスタイルは以下のHTMLのスタイリングに利用されています。これは、地図の左上に表示されている紹介文を表示する部分のエレメントです。
<div class="map-overlay-container">
<div class="map-overlay">
<h2 id="location-title"></h2>
<p id="location-description"></p>
<small>Text credit:
<a target="_blank" href="http://www.nycgo.com/neighborhoods">nycgo.com</a></small>
</div>
</div>
また、以下は地図を表示するエレメントを作成しています。
<div id="map"></div>
Mapの作成
次にJavaScriptのコードを見ていきます。以下のコードはいつも通り、Mapオブジェクトを作成しています。container
で地図を表示するHTMLエレメントのidを指定します。
const map = new mapboxgl.Map({
container: 'map',
// Choose from Mapbox's core styles, or make your own style with Mapbox Studio
style: 'mapbox://styles/mapbox/streets-v12',
center: [-74.0315, 40.6989],
maxZoom: 16,
minZoom: 9,
zoom: 9.68
});
紹介文のための処理
紹介文を表示するためにHTMLのエレメントを取得してtitle
とdescription
という変数でアクセスできるようにしています。
const title = document.getElementById('location-title');
const description = document.getElementById('location-description');
各地点のデータ
次は、各地点のデータです。title
, description
およびcamera
というデータが各地点に含まれているのがわかります。
const locations = [
{
'id': '2',
'title': 'The Bronx',
'description':
"This is where hip-hop was born, where the Yankees became a dynasty and where you can find New York City's leading zoo and botanical garden.",
'camera': {
center: [-73.8709, 40.8255],
zoom: 12.21,
pitch: 50
}
},
...
];
ヘルパ関数の定義
ここでは2個のヘルパ関数が定義されています。
1つ目はレイヤーのフィルターを変更するためのヘルパ関数です。紹介文に該当するポリゴンのみが表示する処理を行います。具体的には、highlight
レイヤーのborocode
プロパティの値がcode
と一致するポリゴンのみが表示されるようになります。
function highlightBorough(code) {
// Only show the polygon feature that corresponds to `borocode` in the data.
map.setFilter('highlight', ['==', 'borocode', code]);
}
2つ目はアニメーションの処理を行うためのヘルパ関数です。title
とdescription
をセットし、ポリゴンを表示させます。またflyTo
でその地点にカメラ(視点)を移動させます。
map.once
はイベント発火時に一度だけ処理を行う際に使用します。map.on
はイベント発火時に毎回行われる処理を登録する機能なので、違いに注意しましょう。
また、ここではmoveend
イベントを使用しています。このイベントは地図上の移動が終了した際に発火します。flyTo
は飛行機で離陸・着陸するようなアニメーションを伴う移動なので、ある程度の時間が必要です。そこで、moveend
イベントでアニメーションが終了したことを検知し、その3秒後にplayback
を再度呼び出して次の地点へと移動します。
function playback(index) {
title.textContent = locations[index].title;
description.textContent = locations[index].description;
highlightBorough(locations[index].id ? locations[index].id : '');
// Animate the map position based on camera properties.
map.flyTo(locations[index].camera);
map.once('moveend', () => {
// Duration the slide is on screen after interaction.
window.setTimeout(() => {
// Increment index, looping back to the first after the last location.
index = (index + 1) % locations.length;
playback(index);
}, 3000); // After callback, show the location for 3 seconds.
});
}
紹介文の初期化
以下のコードでtext
およびdescription
の初期化を行っています。locations
の最後の要素の値で初期化しています。
// Display the last title/description first.
title.textContent = locations[locations.length - 1].title;
description.textContent = locations[locations.length - 1].description;
ソース、レイヤーの作成
load
イベント(map.on('load', () => {})
の中身)で1つのソース、1つのレイヤーを追加しています。
まず、ソースを作成しています。mapbox://mapbox.8ibmsn6uというベクタータイルセットを使用しています。boroughs
というidのソースとして読み込んでいます。
map.addSource('boroughs', {
'type': 'vector',
'url': 'mapbox://mapbox.8ibmsn6u'
});
このデータは、(画面を明るくしないと見にくいですが)以下のようにニューヨークの各自治区のポリゴンデータが含まれるベクタータイルセットです。また、先程出てきたborocode
というプロパティがあることがわかります。レイヤー(ベクタータイルセットにおけるレイヤーとは、データのグループ名です)はoriginal
なので、addLayer
のsource-layer
ではこの値を使用します。
次にレイヤーをhighlight
というidで作成しています。ソースとしては先程のboroughs
を使用し、fill
つまりポリゴンの色と不透明度を設定しています。また、フィルター(filter
)のExpressionsは['==', 'borocode', '']
となっているので一致するデータは存在しない、つまり表示されるポリゴンはありません。
map.addLayer(
{
'id': 'highlight',
'type': 'fill',
'source': 'boroughs',
'source-layer': 'original',
'paint': {
'fill-color': '#fd6b50',
'fill-opacity': 0.25
},
'filter': ['==', 'borocode', '']
},
'road-label' // Place polygon under labels.
);
アニメーションの開始
playback
関数を呼び出すことでアニメーションを開始します。playback
関数の中でhighlightBorough
が実行され、map.setFilter('highlight', ['==', 'borocode', code]);
によってフィルタの条件が変わります。「borocode
プロパティがcode
に一致するポリゴンを表示」というExpressionsになるので、該当するポリゴンのみが表示されます。
playback(0);
まとめ
flytTo
やmoveend
の使い方を確認しました。少し長めのコードでしたが、要素を分解してみると簡単だったかと思います。
おまけ
カメラコントロールの代表的なものに以下の3個があります。
以下はこれらの動きを試すデモです。右上のボタンをクリックすると、東京駅・秋葉原駅間を移動します。
Discussion