ベクトルタイルと言えば MapBox、MapBox といえば ベクトルタイル。
というわけで、以前こんな↓記事を書きました。
Google Maps や Apple のマップアプリでは、もはや自然にベクトルタイルが採用されているわkですが、あれらは当然ながらプロプライエタリ・ソフトウェアであり、ソースは公開されていません。
MapBox は多くのソースコードを公開してくれていて、プログラマにはありがたいかぎりです。
最近では、Android 向け Google Maps で、Web版のような「2本指スクロール」を実現しようとしたのですが、Google Maps SDK にはそのような機能はないので、 MapBox のソースコードを一部改造して実現してみたり。
ということで、前回デバッグ環境を作ったので、今回は地図の描画処理を追ってみたいと思います。 描画のアルゴリズム自体は、必ずしもベクトルタイル特有のものではありませんが、その高速な描画処理は、他へも活かせるノウハウであると思っています。
さて、いろいろすっ飛ばして本題に入りますが、描画に係る主要なソースコードは ./src ディレクトリに点在しています。主な登場人物は、次のとおりです。
Mapbox
は、画面に張り付いている「ビュー」です。
Tile
はみなさんご存知の「タイル」です。データはほとんど保持していなくて空間インデックスで使われる概念的なものです。
Bucket
が、Tile
のデータを表す存在です。前日の記事で @hfu さんが言われている .mvt
や .pbf
のようなベクトルタイルファイルそのものと言ってもよいです。
Layer
はGISでおなじみの地図のレイヤですが、GISでのレイヤほど縛りが強くないというか、地物に付いているタグのような印象を受けました(外しているかもです)。
Painter
は「描画者」で、実際に「WebGL」を使ってBucket
のデータを描画するのが仕事です。
そして WebGL が描画に使用されています。
これらの仕事をシーケンス図風に乱暴に描くと次のようになります。
Tile
群を計算して得ます。Tile
群のデータをロードします。これは即ち .mvt
ファイルをダウンロードして読み込み、内容を Bucket
に変換することです。ここで WebGL ならではの重要なことは、図形の座標データの実体は Bucket
が持つのではなく、WebGL の(GPU上の)「頂点バッファ」と呼ばれる領域にコピーすることです。そして Bucket
は、各図形の座標群として、この「頂点バッファへのインデックス」を持ちます。Bucket
のロードは非同期で行われるため、ロードが完了したときに Mapbox 側に通知を出し、再描画を促します。Mapbox
の描画命令が呼ばれると、ロード済みの Tile
群が対象になります。Mapbox
は Painter
に Tile
の描画を依頼します。Painter
は、Layer
から描画に必要なスタイル情報を得て、それを使用し Tile
内のすべての図形を WebGL を使って描画します。座標データはすでに GPU の頂点バッファにあります。ただしこれは画面座標ではなく、タイル内の相対座標?なので、画面座標へ変換して描画します。この変換処理も WebGL で、つまり GPU で行わせています。このように、GISのライブラリを作ったことがある人ならば、普通のシーケンスであると思われるでしょう。 ただ 「WebGL を使う=GPUの性能をフル活用する」ための工夫が見られます。再度抜き出すと次のようになります。
これらのノウハウは、ガチな描画系のプログラムを描く人には常識でも、私のような「にわか」には得られてよかったことです。
例えば GIS アプリケーションで必要な「投影法」や「座標系」などの変換を、GPU に行わせることはできないだろうかという興味が湧いてきます。 それがデータ変換プログラムなら、変換にかかる時間が短くなることが期待できるし、QGIS のようなデスクトップアプリならば、オンザフライでの投影法変換を行ってもサクサクと動くかも知れません。
今後もベクトルタイルならびに MapBox GL JS/Native の理解を進めて、C# に移植してみたいというのが目下の大きな関心ごとのひとつです。
おまけです。
ソースコードを対象改造して、描画した図形の数をカウントしてみたところ、下図の見た目で 約4500個 でした。ホントかなあ(もっと多そう)という感じがしますが、美麗なスタイルのせいで精度が高い=図形数が多いと錯覚させるのかも知れません。