Leaflet.markerclusterの使い方|大量マーカーをクラスタ表示で高速化

  • マーカーが増えると地図の表示が重くなる
  • ピンが密集して見づらくなる

Leaflet.markerclusterを使えば、近いマーカーを自動でひとまとめにし、ズームすると自動で分解してくれるので、これらの問題をまとめて解決できます

ただし、markerclusterはLeaflet本体(leaflet.js / leaflet.css)を読み込んだ地図があることが前提のプラグインです。

まだLeaflet本体を読み込んでいない場合は、先にそちらを用意してから読み進めてください。

本記事の内容は以下のとおりです。

本記事の内容
  • 導入手順
  • 導入で得られる効果
  • 実装のポイント
  • 通しコードで全体像を確認する

Leaflet.markerclusterの導入手順

手順は次の4ステップです。

  1. CDNでmarkerclusterを読み込む
  2. 地図とタイルレイヤーを初期化する
  3. クラスタグループを作成する
  4. マーカーをまとめて追加する

① CDNでmarkerclusterを読み込む

HTMLの<head>に以下を追加します。

HTML
<link rel="stylesheet" href="https://unpkg.com/leaflet.markercluster/dist/MarkerCluster.css" />
<link rel="stylesheet" href="https://unpkg.com/leaflet.markercluster/dist/MarkerCluster.Default.css" />
<script src="https://unpkg.com/leaflet.markercluster/dist/leaflet.markercluster.js"></script>

このスクリプトは、地図本体のLeaflet(leaflet.js)を読み込んだあとに読み込む必要があります。順番を逆にすると動きません。

② 地図とタイルレイヤーを初期化する

地図の土台を作ります。

JavaScript
const map = L.map("map", {
    center: [35.68, 139.76],
    zoom: 11,
});

L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
    attribution: "© OpenStreetMap contributors",
}).addTo(map);

前半のL.map()で地図の土台を作り、後半のL.tileLayer()でタイル画像を重ねている、という2段構えです。

③ クラスタグループを作成する

ここがmarkerclusterのポイントです。マーカーを地図に直接追加するのではなく、いったんクラスタグループに追加します。

JavaScript
const markerClusterGroup = L.markerClusterGroup();
map.addLayer(markerClusterGroup);

L.markerClusterGroup()で専用のレイヤーを作り、それを地図に追加しておく、というわけです。個々のマーカーは、この後この markerClusterGroup のほうに追加していきます。

④ マーカーをまとめて追加する

手持ちのデータからL.marker()を作り、地図ではなくmarkerClusterGroupのほうにaddLayer()します。

JavaScript
const ALL_PARKS = [
    { id: 1, name: "中央公園", lat: 35.689, lng: 139.700 },
    { id: 2, name: "西公園",   lat: 35.665, lng: 139.730 },
    // ... 最大数千件まで対応
];

const MAX_MARKERS = 1000; // 任意
const parksForMap = ALL_PARKS.slice(0, MAX_MARKERS);

parksForMap.forEach((park) => {
    if (!park.lat || !park.lng) return;

    const marker = L.marker([park.lat, park.lng]);

    marker.bindPopup(
        "<strong>" + park.name + "</strong><br>" +
        '<a href="/park/' + park.id + '/">詳細を見る</a>'
    );

    markerClusterGroup.addLayer(marker);
});

MAX_MARKERSで件数を絞っているのは、あくまで念のためです。markerclusterは数千件規模でも十分軽快に動きますが、データ量が読めない場合の安全弁として入れておくと安心です。

導入で得られる効果

markerclusterを導入すると、次のような効果が得られます。

  • 地図が重くならずサクサク動く
  • ピンの密集がクラスタとして見えるので視認性向上
  • 既存コードにほぼ手を加えず導入可能
  • APIアクセスが増えずバックエンドに優しい

実装のポイント

実装するうえで押さえておきたいポイントは次の4つです。

  • 初期表示はややズームアウトしてクラスタが見える状態にする
  • パフォーマンスのために最大マーカー数を設定することも可能
  • カード連動(クリック・フォーカス)はID管理で簡単に実現可能
  • データが非常に増えたら「地図範囲絞り API」方式へ移行もできる

ちなみに実装中、複数のマーカーを高速でクリックすると意図しない挙動になる「クリック時の競合問題」にぶつかりました。この対処法は別記事にまとめています。

通しコードで全体像を確認する

断片的なコードだけでは見えない部分も、一連の流れで並べて見ると分かりやすくなります。ここまでのコードをまとめたサンプルです。

JavaScript
const map = L.map("map", {
    center: [35.68, 139.76],
    zoom: 11,
});

L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
    attribution: "© OpenStreetMap contributors",
}).addTo(map);

const markerClusterGroup = L.markerClusterGroup();
map.addLayer(markerClusterGroup);

const ALL_PARKS = [
    { id: 1, name: "中央公園", lat: 35.689, lng: 139.700 },
    { id: 2, name: "西公園", lat: 35.665, lng: 139.730 },
    // ... 最大数千件まで対応
];

ALL_PARKS.forEach((park) => {
    const marker = L.marker([park.lat, park.lng]);
    marker.bindPopup(`<strong>${park.name}</strong>`);
    markerClusterGroup.addLayer(marker);
});

地図の初期化 → クラスタグループの作成 → マーカーの追加、という3つの塊で構成されています。

まとめ

Leaflet.markerclusterを使えば、大量のマーカーがある地図でも、表示を重くせず見やすく保てます。まずは導入手順の4ステップから試してみてください。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

普段は主にPython開発をしています。
最近はAI駆動開発にも関わっています。

コメント

コメントする