💬

Mapbox Newsletter WEEKLY TIPSの解説 -「ホバー時にポップアップを表示」

2024/07/02に公開

はじめに

この記事は、先日配信されたMapbox NewsletterのWEEKLY TIPSで紹介されていた「ホバー時にポップアップを表示」についての解説です。このサンプルではPopupおよびマウスイベントの使い方について例示しています。また、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>

以下はポップアップのスタイルです。.mapboxgl-popupというクラスはPopupのコンテナのdivを指します。

.mapboxgl-popup {
  max-width: 400px;
  font:
    12px/20px 'Helvetica Neue',
    Arial,
    Helvetica,
    sans-serif;
}

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: [-77.04, 38.907],
  zoom: 11.15
});

ソース・レイヤーの作成

今回のデモは事前に複数の地点を登録し、そこにマウスカーソルを合わせるとポップアップが表示されます。そのため、複数地点をあらかじめ作成しておきます。ソースとレイヤーはスタイルが読み込まれた後に作成する必要があるため、map.on('load', () => { /* ここ */})の部分に処理を書きます。

ソースの作成

Map#addSourceでソースを作成します。第1引数が任意のソースID、第2引数がソースになります。ここではGeoJSONをソースとして使用しています。FeatureCollectionを用いて、複数のPointデータを登録しています。

map.addSource('places', {
  'type': 'geojson',
  'data': {
    'type': 'FeatureCollection',
    'features': [
      {
        'type': 'Feature',
        'properties': {
          'description':
            '<strong>Make it Mount Pleasant</strong><p>Make it Mount Pleasant is a handmade and vintage market and afternoon of live entertainment and kids activities. 12:00-6:00 p.m.</p>'
        },
        'geometry': {
          'type': 'Point',
          'coordinates': [-77.038659, 38.931567]
        }
      },
...中略...
    ]
  }
});

レイヤーの作成

Map#addLayerでレイヤーを作成します。ここではCircleレイヤーを作成しています。また、sourceは先程のソースのIDを指定します。

map.addLayer({
  'id': 'places',
  'type': 'circle',
  'source': 'places',
  'paint': {
    'circle-color': '#4264fb',
    'circle-radius': 6,
    'circle-stroke-width': 2,
    'circle-stroke-color': '#ffffff'
  }
});

Popupの作成

次に、Popupをあらかじめ作成しています。closeButton: falseで右上の✗ボタンを非表示、closeOnClick: falseでクリックでPopupを閉じる挙動を防止しています。

const popup = new mapboxgl.Popup({
    closeButton: false,
    closeOnClick: false
});

ホバー時の動作

マウスカーソルがCircleレイヤーの地点上に来たときにPopupを表示するのが目標です。そこでここではmouseenterイベントを使用しています。map.on('mouseenter', レイヤーID, () => { /* ここ */})のように使用します。第2引数に監視対象とするレイヤーのIDを指定します。マウスカーソルがそのレイヤーの表示領域に入ると「ここ」の処理が発火します。

処理内容を確認します。

まず、マウスカーソルの形状を変えています。

// Change the cursor style as a UI indicator.
map.getCanvas().style.cursor = 'pointer';

コールバック関数の引数eMapMouseEventです。その中のfeaturesはイベントが起きたときのそのレイヤーのフィーチャーが格納されています。そこで、座標とプロパティを取得しています。

// Copy coordinates array.
const coordinates = e.features[0].geometry.coordinates.slice();
const description = e.features[0].properties.description;

以下の処理はプロジェクション(地図投影法)がmercatorのときには必要な処理です。

// Ensure that if the map is zoomed out such that multiple
// copies of the feature are visible, the popup appears
// over the copy being pointed to.
while (Math.abs(e.lngLat.lng - coordinates[0]) > 180) {
    coordinates[0] += e.lngLat.lng > coordinates[0] ? 360 : -360;
}

mercatorでは以下のように横方向に地図が繰り返し表示されますが、上記処理がないと右のCircleにマースカーソルを合わせたときに左側にPopupが出てしまいます。
mercator

ただし、Mapbox Streets v12ではプロジェクション(地図投影法)はglobeがデフォルトであり、この処理は不要です。そこで、サンプルコードからも削除することが検討されています。

最後にPopupの表示する座標、内容を設定してMapオブジェクトにaddToすることで表示されます。

// Populate the popup and set its coordinates
// based on the feature found.
popup.setLngLat(coordinates).setHTML(description).addTo(map);

マウスアウト時の動作

マウスカーソルがCircleレイヤーから外れたときはmouseleaveイベントを使用しています。マウスカーソルをデフォルトに戻し、Popup#rempveでPopupを削除します。

map.on('mouseleave', 'places', () => {
    map.getCanvas().style.cursor = '';
    popup.remove();
});

まとめ

PopupはMarkerといっしょに使うことが多いですが、単独でも使用できることがわかりました。また、マウスイベントの扱い方についても確認しました。

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

Discussion