Mapbox GL JS の描画の仕組み

ベクトルタイルと言えば MapBox、MapBox といえば ベクトルタイル。

というわけで、以前こんな↓記事を書きました。

Google Maps や Apple のマップアプリでは、もはや自然にベクトルタイルが採用されているわkですが、あれらは当然ながらプロプライエタリ・ソフトウェアであり、ソースは公開されていません。

MapBox は多くのソースコードを公開してくれていて、プログラマにはありがたいかぎりです。

最近では、Android 向け Google Maps で、Web版のような「2本指スクロール」を実現しようとしたのですが、Google Maps SDK にはそのような機能はないので、 MapBox のソースコードを一部改造して実現してみたり。

MapBox GL JS の描画処理を追ってみる

ということで、前回デバッグ環境を作ったので、今回は地図の描画処理を追ってみたいと思います。 描画のアルゴリズム自体は、必ずしもベクトルタイル特有のものではありませんが、その高速な描画処理は、他へも活かせるノウハウであると思っています。

さて、いろいろすっ飛ばして本題に入りますが、描画に係る主要なソースコードは ./src ディレクトリに点在しています。主な登場人物は、次のとおりです。

  • Mapbox
  • Tile
  • Bucket
  • Layer
  • Painter
  • WebGL

Mapbox は、画面に張り付いている「ビュー」です。 Tile はみなさんご存知の「タイル」です。データはほとんど保持していなくて空間インデックスで使われる概念的なものです。 Bucket が、Tile のデータを表す存在です。前日の記事で @hfu さんが言われている .mvt.pbf のようなベクトルタイルファイルそのものと言ってもよいです。 Layer はGISでおなじみの地図のレイヤですが、GISでのレイヤほど縛りが強くないというか、地物に付いているタグのような印象を受けました(外しているかもです)。 Painter は「描画者」で、実際に「WebGL」を使ってBucketのデータを描画するのが仕事です。 そして WebGL が描画に使用されています。

これらの仕事をシーケンス図風に乱暴に描くと次のようになります。

image.png

  1. ユーザーの操作などで地図の表示範囲が変更されると、その範囲に触れる Tile 群を計算して得ます。
  2. 得られた Tile 群のデータをロードします。これは即ち .mvt ファイルをダウンロードして読み込み、内容を Bucket に変換することです。ここで WebGL ならではの重要なことは、図形の座標データの実体は Bucket が持つのではなく、WebGL の(GPU上の)「頂点バッファ」と呼ばれる領域にコピーすることです。そして Bucket は、各図形の座標群として、この「頂点バッファへのインデックス」を持ちます。
  3. Bucket のロードは非同期で行われるため、ロードが完了したときに Mapbox 側に通知を出し、再描画を促します。
  4. Mapbox の描画命令が呼ばれると、ロード済みの Tile 群が対象になります。
  5. MapboxPainterTile の描画を依頼します。
  6. Painter は、Layer から描画に必要なスタイル情報を得て、それを使用し Tile 内のすべての図形を WebGL を使って描画します。座標データはすでに GPU の頂点バッファにあります。ただしこれは画面座標ではなく、タイル内の相対座標?なので、画面座標へ変換して描画します。この変換処理も WebGL で、つまり GPU で行わせています。

このように、GISのライブラリを作ったことがある人ならば、普通のシーケンスであると思われるでしょう。 ただ 「WebGL を使う=GPUの性能をフル活用する」ための工夫が見られます。再度抜き出すと次のようになります。

  • データの実体を GPU 上に持ち、ムダなコピーを持たない
  • 座標変換などの計算もなるべく GPU にやらせる

これらのノウハウは、ガチな描画系のプログラムを描く人には常識でも、私のような「にわか」には得られてよかったことです。

例えば GIS アプリケーションで必要な「投影法」や「座標系」などの変換を、GPU に行わせることはできないだろうかという興味が湧いてきます。 それがデータ変換プログラムなら、変換にかかる時間が短くなることが期待できるし、QGIS のようなデスクトップアプリならば、オンザフライでの投影法変換を行ってもサクサクと動くかも知れません。

今後もベクトルタイルならびに MapBox GL JS/Native の理解を進めて、C# に移植してみたいというのが目下の大きな関心ごとのひとつです。

そもそも図形数が少ないのではないか説

おまけです。

ソースコードを対象改造して、描画した図形の数をカウントしてみたところ、下図の見た目で 約4500個 でした。ホントかなあ(もっと多そう)という感じがしますが、美麗なスタイルのせいで精度が高い=図形数が多いと錯覚させるのかも知れません。

image004.png