TL;DR
- モダンなウェブフレームワークでは、「宣言的」なコードがうれしい
-
Map.addLayer/removeLayer
を利用すると「手続き的」なコードとなってしまう -
Map.setStyle(newStyle)
を利用すると、宣言的にレイヤーを定義できる
はじめに
MapLibre User Group Japanの@Kanahiroです。
MapLibreとは、モダンなWeb地図にまつわるOSSを傘下に収める開発者コミュニティです。
この記事について
現代のWeb開発ではもはや、ReactやVueといったフレームワーク(ないしライブラリ)の利用は当たり前となりました。当然、MapLibre GL JSを利用する場合はそれらと組み合わせて開発するわけですが、適切なAPIを用いた「ちょっと賢そうな」実装を紹介します。
モダンなフレームワークと地図ライブラリ
ReactやVue、最近ではSvelteが人気ですが、これらは、JavaScriptの変数とUIを同期させやすくしている、と理解できると思います。これは、JavaScriptの変数を監視して変更をUIに反映している…、と言えます。
UIがDOMなら、フレームワークの作法に沿うだけで、簡単にUIを更新出来るでしょう。しかし地図ライブラリの場合はちょっと違って:
となります。LeafletやMapLibre GL JSだとaddLayer
なんて関数があり、これを利用するようなコードを書くことになります。Reactだと以下のようになるでしょうか(疑似コードです)
// layersという、表示するレイヤーを保存する配列があるとして
useEffect(() => {
const newLayer = getNewLayer(layers); // 追加されたレイヤーだけを抽出
map.addLayer(newLayer);
}, [layers])
もちろんremoveLayer
の場合も考慮する必要があるでしょう。
この実装はかなり面倒です:
- layersの変更を検知して、何が増えたのか・何が減ったのか、判別する必要がある
- すでに存在する(しない)レイヤーをadd(remove)してしまうとエラーが起きるので、存在チェックが必要
という問題があるためです。
これらの根本原因は、フレームワークで監視している「状態」と、mapインスタンス内の「状態」が独立しているためです。
MapLibre GL JSのちょっと賢いレイヤー管理
MapLibre GL JSではMap.setStyle(newStyle)
という関数が提供されています。変更後のスタイルを引数として関数を実行すると、地図の状態がそのスタイルの状態に書きかわります。
なのでさっきと同様に疑似コードを書くと
// mapStyleという、地図スタイルを保持する変数があるとして
const [mapStyle, setMapStyle] = useState({
version: 8,
layers: [],
source: {}
});
useEffect(() => {
map.setStyle(mapStyle);
}, [mapStyle])
となります。こうすると、たとえばmapStyle.layers
が変化したら、map.setStyle(mapStyle)が実行され、mapに新しいスタイルが適用されることになります。するとさっきに比べて:
- 何が増えたのか・減ったのか、考える必要がない
- すでに存在する(しない)レイヤーをadd(remove)してしまうことはない
という嬉しいことがあります。これは常に「表示すべき地図スタイルを定義すればよい」ということです。すなわち「宣言的」なコードとなります。どれだけたくさんのレイヤーがあっても、管理しやすいコードになるでしょう。
なお、setStyle
は、前後の差分のみを変更する処理を行うので、パフォーマンスにも優れています(=全てのレイヤーを削除して再追加したりしない)。
終わりに
地図ライブラリはモダンなフレームワークでは「副作用」であるため、うまく使わないと色々難しいことが起きると思います。今回の記事でうまく使う手法を紹介しました。
また、たとえばReactならreact-map-gl
というライブラリがあります。
これはまさに、「副作用」をラップしてReactの「宣言的な世界」で使いやすくしているものです。こういったライブラリを利用するのも、よいプラクティスです。