はんけトケのロゴタイプ
これは何?
開発ノート

描画の改善

画面がカクつく

MapLibreの描画に関して、案外特殊なケースなのか、情報がなかったため共有します。 泥縄的で最適解ではありませんが、参考までに。

はんけトケのピンをドラッグできるようにしたときのことです。 画面が妙にカクつくことに気づきました。 フルスクリーンにしたとき特に顕著です。 原因はすぐに判明。 ピンそのものではなく、ピンにくっついて動く範囲円のせいでした。

円の描きかた

はんけトケの範囲円は、位置・大きさが変化するたびに円形のGeoJSONを生成し、MapLibreのGeoJSONレイヤーにセットすることで描く仕組みでした。

単なる円とはいえ、見かけの大きさは地図上の緯度やビューに応じて変わるし、パンやズームにも同期せねばならないので、こうして地図上の図形としてMapLibreに描いてもらっていたのです。 ラクで確実、公式サンプルコードにもある標準的な方法です。

ところが。

MapLibreのクセ

どんなに小さく単純でも、GeoJSONレイヤーに図形をセットすると、そのたびMapLibreは画面全体を再描画します。 ビューが変化するのだから、レンダリングし直すのは当然ではあります。

ただ、これがやけに重い。 内部の処理は知りませんが、表面的な現象としては、どうやら全体の表示面積(ウインドウの大きさ)に比例して重くなるようです。

そういえばWebGLは、どういうわけか表示面積の大きさに弱い傾向があります。 描くピクセル数が多いほど負荷も多いのは当然ですが、そんな常識的なことを言っているのではありません。 たとえ簡単な描画内容でも、ブラウザーをフルスクリーンにしただけで露骨に処理落ちしたりするのです、しかもゲーミングPCでも。

それとは別に、CPU側に何らかの負荷がかかっている可能性もあります。 たとえばMapLibreの公式ドキュメントでは、テキストラベルの重なり判定の重さに言及していたりします(本件には当てはまらないが)。

タスクマネージャーを見るかぎりGPU側の負荷のようですが、犯人捜しはさておいて。 GeoJSONレイヤーなんてそうそう変更されないのが一般で、この現象はふつうは問題になりません。 が、範囲円をグリグリ動かし、その都度GeoJSONを高速に書き換えまくる(必要以上にしない工夫はしている)はんけトケでは、このことは大問題。

いろいろ調べたり試したりしたものの、GeoJSONがビューに組み込まれている以上、回避のしようがないようです。

deck.glを導入

かくなるうえは、MapLibreとは別口で地図に円を描くしかありません。 がんばれば自前で実装できるかもしれませんが、円ひとつのためにがんばりたくありません。 そこで、「deck.gl」を導入してみることにしました。 deck.glには、「MapLibreと同期する独自のレイヤーを追加」する機能があるらしいのです。

結果、うまくいきました。 deck.glで描く円はスムーズに動きます。 ミソは、deck.glを「インターリーブ」ではなく「オーバーレイ」モードで組み込むこと。

オーバーレイモードでは、MapLibreのキャンバスの上に、deck.glのキャンバスが文字どおり単純に被さります。 キャンバスが別なのだから、deck.gl側でいくら円を更新しても、MapLibre側に再描画の必要はないわけですね。 ちなみに、deck.glでの円描画にはGeoJSONではなくScatterplotを使いました。

MapLibreの制御下にあるピン(マーカー)は、もともと独立したDIV要素なので、z-indexをいじっていちばん上に表示します。

ただし……

良好な結果ですが、ごくまれに、何かの拍子にdeck.glのレイヤー座標がずれてしまうことがあります。 ビューのリサイズにまつわるバグかなと思いつつ(ウインドウをリサイズすると直ったりする)、今のところ明確な再現性がなく対処不能。 deck.glの今後の更新で自然に直るかもしれませんが、あまりたびたび起こるようなら表示リセットボタンでも用意したほうがいいのかも。 しばらく様子を見ます。

レイヤーも見直す

たかだか円を描くだけで面倒なことになったものです。 しかし手をかけただけの値打ちはありました。

これを機に、MapLibre自体のレイヤー構成も見直しました。 もともとレイヤー数が少なく済むようエクスプレッションを工夫しつつ、地図を情報で色分けしたりしていました。 それをやめ、シンプルに地図タイルだけを表示するようにしたのです。

特に、マウスオーバーでハイライトするような、再描画が頻繁にかかるエクスプレッションは絶対避けることにしました。 ありふれた仕掛けで一見問題なさそうでも、しょぼいPCで広い画面に映してみると、やっぱりカクカクするからです。

多少カクついても色分けによる便利さが勝る局面もあるでしょう。 しかし、はんけトケでは軽快さ・手ざわりのよさが最優先。 MapLibreの描画処理が改善されたり、ちまたの機器の性能がもっと底上げされたら再考したいと思います。

用語・注釈

GeoJSON
経緯度を座標系とする点・線・面を定義できる、JSON形式の地理情報データ。
WebGL
ウェブ用のGPUグラフィックスライブラリー。 MapLibreはこれを利用して高速描画を実現している。
deck.glについて
機能豊富でサイズの大きなライブラリーを、円ひとつ描くためだけに導入するのはむろん非効率。 必要な機能のみビルドし直せば多少ましになるが、その部分と方法がわからない。 前向きに、せっかく導入したからにはもっと活用しよう、と考えてはいる。
エクスプレッション
MapLibreの地図表示スタイルを制御するための構文。