PMTilesをC#で実装する
きっかけ
前回の開発ログで、ベクターマップを実装しました。
前回使用したタイルはpbfというファイル群によって提供されているのですが、実はこれの他に「PMTiles」という形式のファイルがあります。
PMTilesはサーバー側で扱いやすいのが特徴です。詳細については後で話します。
私がPMTilesに出会ったきっかけは、国土地理院最適化ベクトルタイルにありました。
マップの読み込みの遅さに悩んでいたところ、最適化ベクトルタイルというものに出会い、このリポジトリを見つけたのですが、このときは既にpbfファイル群の更新は終わっており、PMTilesとしての更新に移り変わっていたのです。
ガーン、、、実はPMTilesを扱う.NETのライブラリはないのです!これでは最適化ベクトルタイルは使用し難いです...あら残念。
PMTiles.NETの作成へ
ここで諦めないのが私なんですよ。
PMTilesの公式リポジトリがこちらにあります。
主にNode.js (TypeScript) をサポートしています。私が持っている60%のTypeScriptを理解する能力でこれを読んで理解し、私が持っている90%のC#を書く力で実装する必要があるのです。
誰か開発一緒に手伝ってください... (ぜひコメントに)
不完全ですが、読み取りの方は実装できました。
NuGetで公開しているのでぜひ使ってみてください
https://www.nuget.org/packages/PMTiles.NET
using PMTiles;
string url = "https://cyberjapandata.gsi.go.jp/xyz/optimal_bvmap-v1/optimal_bvmap-v1.pmtiles"; // 地理院地図最適化ベクトルタイル
using (PMTilesReader reader = await PMTilesReader.FromUrl(url)) {
byte[] bytes = await reader.GetTileZxyAsBytes(9, 454, 201);
// 何かする
}
こんな風に使えます。できるだけ便利に使えるように頑張りました。
ちなみにこれGitHubの範囲で調べたところ、僕が世界初のようです。
なんで作られてないのか不思議ですが...
PMTilesの特徴
PMTilesのindex.tsを読んでいて気付いたのですが、PMTilesには様々な工夫が施されています。
1. 1つのファイルで完結
PMTilesはなんと大量のファイルを用意することなく、 .pmtilesの1つのファイルだけで済みます。
ここまではMBTilesと同じように見えますが、独自仕様を用いており、データベースなどの特別な環境が必要ないのです。
2. 範囲リクエストを活用
HTTPでは、コンテンツの範囲を指定してサーバーにその部分のみを返してもらうことができます。これを"Range Request"というらしいです。(ここでは範囲リクエストと呼ばせてもらいます)
前述の通り、PMTilesは1つのファイルにまとめられているため、サイズはとても大きいです。例えば、地理院地図最適化ベクトルタイルは 16,957,904,981bytes (15.7GB)あります。(これでも小さい方)
毎回こんな莫大なファイルを全部取得していたら日が暮れてしまいます。
PMTilesは必要な場所だけを範囲リクエストで取得しています。
ではPMTilesはどうやってその必要な範囲を識別しているのでしょうか?
- ヘッダー
このタイルの情報を定義しています。
例: 圧縮方式、タイルの総数、緯度経度の範囲 - ルートディレクトリ
タイルごとの範囲リクエストを行うべき場所を定義します。 - リーフディレクトリ
ルートディレクトリに収まらなかった場合に、ルートディレクトリからここに参照が伸びます。 - タイルディレクトリ
タイルが入っているところです。
クライアントはまずヘッダーを取得し、それぞれのディレクトリの範囲を把握します。
次に、欲しいタイルのIDをルートディレクトリ+リーフディレクトリから探し、そのタイルの場所を調べます。
最後に、タイルディレクトリからその場所を参照し、対象のデータを取得します。
3. 圧縮
PMTilesでは内部で圧縮をふんだんに使っています。
ヘッダー以外では、GZip, Brotli, Zstdの圧縮を使うことができます。(ほとんどの場合はGZipですが...)
また、圧縮率が高くなるようにタイルの配置や方向が工夫されているようです。
まとめ
今回はPMTilesをC#の方で実装してみました!
何気に本格的なパッケージを公開するのは今回が初めてなので、色々面白かったです!(リファクタリング多めにしたり英語でコミットメッセージを書いてみたりNuGet.orgに登録したり...)
今後も(時間があれば)いくつかライブラリを作ってみたいと思います。
次回はMapbox Vector Tilesについて勉強しようと思います。ここまで読んでくださりありがとうございました!