
MapLibre GL JS + Svelte でカスタムマーカーを表示する
#プログラム#MapLibre#SvelteMapLibre GL JS + Svelte を使って、カスタムマーカーを表示する。カスタムマーカーと言いつつ、HTML要素を表示できるので、活用の幅は広い。
プロジェクト作成
説明用のプロジェクトを作成する。以前の記事の手順通りにプロジェクトを作成し、 src/App.svelte
を以下のように書き換える。
<script lang="ts">
import maplibregl, { Map } from "maplibre-gl";
import { onMount } from "svelte";
let map: Map;
// マウント後に地図を初期化する。
onMount(() => {
map = new maplibregl.Map({
container: "map",
style:
"https://tile.openstreetmap.jp/styles/maptiler-basic-ja/style.json", // スタイル指定
center: [139.766, 35.682], // 初期表示位置。 [lng, lat] で経度・緯度の順なので注意
zoom: 16, // 初期表示拡大率
dragRotate: false,
});
// 拡大縮小コントロールを表示する。
map.addControl(new maplibregl.NavigationControl({ showCompass: false }));
const marker = new maplibregl.Marker({})
.setLngLat([139.7659, 35.6814])
.addTo(map);
});
</script>
<svelte:head>
<link
href="https://unpkg.com/maplibre-gl@2.4.0/dist/maplibre-gl.css"
rel="stylesheet"
/>
</svelte:head>
<main>
<div id="map" />
</main>
<style>
/* ページの余白を削除する */
:global(#app) {
margin: 0;
padding: 0;
}
/* 画面いっぱいに地図を表示する */
#map {
position: fixed;
top: 0;
left: 0;
width: 100dvw;
height: 100dvh;
}
</style>

デフォルトマーカーの表示
上記コードではマーカーを地図に追加している。詳細な位置が分かりやすいように、東京駅の丸の内側の中央の位置に置いている。
マーカーの生成時 new maplibregl.Marker({})
に、何もオプションを指定していない。このようにカスタムマーカーとしてのHTML要素を指定していない場合は、デフォルト形状のマーカーが表示されるようになっている。
ただこのマーカー、クリックされたときの動作を仕込めない。ドキュメントを見ると分かる通り、ドラッグ操作に対するイベントしか対応していない。これを解決するためにはカスタムマーカーを使用することになる。
マーカーへのポップアップの追加
今回の記事の本題とはズレるものの、もしデフォルトマーカーに対して追加情報を表示したい場合、ポップアップが使える。クリックすると追加情報がポンと表示されるあれ。
コードとしては、マーカーを追加する部分を、以下のように書けば良い。
const popup = new maplibregl.Popup().setHTML(
"<h2>東京駅 丸の内口</h2>東京駅の皇居側<br />広場がある"
);
const marker = new maplibregl.Marker({})
.setLngLat([139.7659, 35.6814])
.setPopup(popup) // 追加。マーカーにポップアップを設定する。
.addTo(map);
マーカーをクリックすると、設定したHTML要素の中身を持つポップアップが表示される。

カスタムマーカー
カスタムマーカーを使えば、見た目を自由に設定できる。というか、マーカーとしてHTML要素を設定できるので、画像だけではなく文字列を表示したり、イベント処理を組み込んだり、みたいなことが可能。
やり方は簡単で、マーカー生成時のオプションで element
に対して HTMLElement
を渡せば良い。
カスタムマーカーの追加方法
実際に上記のコードに対して、カスタムマーカーを表示してみる。
主な変更点は以下の2箇所。
マーカーとなる要素の生成と追加
まずはマーカーとなるHTML要素 el
(型は HTMLDivElement
)を生成し、クラスや中身を指定している。
そしてマーカー生成時に、オプションの element
へ el
を渡している。
// マーカーとして使用するHTML要素を生成する。
const el = document.createElement("div");
el.className = "custom-marker";
el.innerHTML = "<h2>東京駅 丸の内口</h2>東京駅の皇居側<br />広場がある";
// マーカー作成時に、オプションとしてHTML要素を指定する。
const marker = new maplibregl.Marker({ element: el })
.setLngLat([139.7659, 35.6814])
.addTo(map);
CSSの設定
そして <style>
の中に以下の記述を追加する。
:global(.custom-marker) {
background: hsla(200, 50%, 50%, 0.5);
padding: 0 1em;
cursor: pointer;
}
マーカーとなるHTML要素を custom-marker
クラスとしたので、これに対するスタイルを書く。スタイルの中身としては、背景色と余白、それとマウスカーソルを合わせたときの形状を設定している。
ここでのポイントが、 :global()
にスタイルを指定していること。Svelte のスタイルはそのコンポーネントにのみ適用されるため、子要素になるマーカーには適用されない。したがって :global()
を使ってコンポーネント外にも適用されるようにしている。
これで実行すると、以下のようにカスタムマーカーが表示される。単なる四角いエリアであり、マーカーとしての見た目をしていないけど。

マーカーをクリックしたときのイベント設定
このようにカスタムマーカーを指定すれば、デフォルトマーカーではできなかった、マーカーがクリックされたときのイベントに対応可能。
マーカーとして指定したHTML要素に対してクリック時のイベントを設定すればよいというわけで、以下のように el
に対してのイベントリスナを設定する。これでマーカーとなっている要素をクリックしたとき、ブラウザの開発者ツールのコンソールに文字列が表示される。
el.addEventListener("click", () => {
console.log("マーカーがクリックされました");
});
これを使えば色々できるので、例えばクリックされたマーカーを消したりできる。マーカーの生成後、以下のようにイベントリスナを設定すれば良い。
el.addEventListener("click", () => {
marker.remove();
});
マーカーの表示位置の調整
現状では、マーカーとして指定した座標に、カスタムマーカーのHTML要素の中心が位置していることが分かる。これがデフォルトの動作。でも自作マーカーでは位置をずらしたいことはよくあるということで、オプションを渡すことで調整可能。
まずは anchor
があり、これはHTNL要素のどの位置が指定した座標に来るかを設定できる。文字列での指定となり、'center' , 'top' , 'bottom' , 'left' , 'right' , 'top-left' , 'top-right' , 'bottom-left' , 'bottom-right'
のいずれかとなり、デフォルト(無指定)では'center'
になっている。
さらにそこからずらしたいときは offset
を使える。これは何ピクセルずらすかを指定できる。
具体的な例として、マーカー要素の中に、位置をピンポイントで示すピンを表示するとする。マーカーのHTML要素の子要素として、絶対座標のピンとして角丸を追加する。ピンの位置は左右中央で、上から4ピクセルの位置に上端が来るような設定としている。
// マーカー要素内のピン。
const pin = document.createElement("div");
pin.style.cssText =
"width: 12px; height: 12px;" +
"border: 2px solid hsl(160, 50%, 50%); border-radius: 6px;" +
"position: absolute; top: 4px; left: 50%;" +
"transform: translateX(-50%);";
const el = document.createElement("div");
el.className = "custom-marker";
el.innerHTML =
"<br /><b>東京駅 丸の内口</b>東京駅の皇居側<br />広場がある<br /><br /><br /><br />";
el.appendChild(pin); // 子要素として追加
const marker = new maplibregl.Marker({ element: el })
.setLngLat([139.7659, 35.6814])
.addTo(map);

ピンの角丸が表示されているものの、ピンで示したい位置からずれている。これは前述の通り、HTML要素の中央が指定した座標に来ているため。これを上手いことずらして、ピンの位置を合わせるようにする。
今回はピンが左右中央で上部から一定のピクセルに来るということで、まずは anchor
として top
を指定する。これでHTML要素の上部・左右中央が座標の位置になる。
これからさらにマーカー全体を少し上にずらす。HTML要素内の上部から見たピンの中心位置は、余白4px + 線幅2px + 大きさの半分6px の合計12ピクセルなので、12ピクセル上にずらせば良い。したがって offset: [0, -12]
を指定する。
それを反映したのが以下のコード。これで意図した位置にマーカーが表示される。
const marker = new maplibregl.Marker({
element: el,
anchor: "top",
offset: [0, -12],
})
.setLngLat([139.7659, 35.6814])
.addTo(map);

MapLibre ではこのようにしてカスタムマーカーを利用できる。
マーカーとしてHTML要素を表示できるので、文字や画像をそのまま表示できる。つまりマーカーピンの見た目を変えられるだけではなく、追加の情報を表示したり、使い道の幅は広い。