GeoJSONをフロントエンドでベクトルタイル化して表示する。

中規模のデータをどうやって地図上に視覚化するか

フロントエンドで地図を表示するのは、データサイズとの戦いです。
現在のところ、大量のデータを表示するにはベクトルタイル化するのがもっともベターですが、ベクトルタイルにもいくつかの何点があります。

例)全国のガソリンスタンドの位置を地図上にプロットしてみた。

一つは、データのコンバート作業に時間がかかること。
どの程度のズームレベルまで作成するかによるのですが、タイルファイルを生成するまでには何工程か経なければならず、運用で頻繁にデータを差し替えたり更新したりする必要がある場合、無視できない作業量になります。

もう一つは、フロントエンドでかなり多くの404アクセスエラーがでること。
タイルのコンバート時間を短くするには、必要な部分のみ作成するのがもっとも効果的なのですが、その場合、地図クライアントソフトが存在しないタイルに対してもリクエストを投げてしまうためコンソールに大量のアクセスエラーがでてしまいます。

動作には問題ないのですが、精神衛生上あまりよくありません。
もちろん、全世界分、全ズームレベル分のタイルを生成すればエラーはでなくなりますが、当然コンバート時間が膨れ上がります。

と言った感じで、「そのままでは大きすぎるんだけど、ベクトルタイルにコンバートするには小さい」といったサイズのデータが一番扱いにこまることが多いです。
(特にデータビジュアライゼーションでは、この規模のデータを使うこと非常に多い)

そこで、そのまま表示するには大きすぎるGeoJSONを、フロントエンドでベクトルタイル化して表示するということをやってみました。

geojson-vt

geojson-vt

geojson-vtはMapbox社がオープンソースとして公開しているライブラリで、ブラウザ上でGeoJSONデータをスライスしベクタータイルデータを生成することができます。
使い方は非常に簡単で、 GeoJSONデータをgeojsonvtメソッドに渡してあげれば、あとは任意のズームレベル・タイル座標を指定することでスライスされたGeoJSONデータを取得できます。

このライブラリを使って、フロントエンドでGeoJSONをベクトルタイル化しLeafletで表示するサンプルを作成しました。

サンプル1

市区町村境界データ(GeoJSON)を、読み込みサイズを減らすためにGeoBuf化し、フロントエンドでgeojson-vtを使ってベクトルタイル化、LeafletのgridLayerを使ってcanvasタイルとして表示しています。

example

SVGLaeyrに市区町村レベルのポリゴンを載せると、動作を軽くするためにかなりの簡素化が必要でズームして言った時にポリゴン欠けが目についたのですが、この方法だとほぼ目立たなくなっています。
中規模のデータを視覚化したい時の選択肢の一つになりそうです。

サンプル2

SVGでなくCanvasに地図を描画した場合の問題点として、クリックイベント等が取りづらくなるという点が挙げられます。
クリックイベント自体は問題なく取得できますが、SVGのようにエレメントが境界に合わせて生成されるわけではないので、描画した地図のどの部分がクリックされたのをクリック座標から判別する機能を自分で実装しなくてはなりません。
意外と面倒な作業なのですが、Deck.glのドキュメントに掲載されていた”color picking”という手法がシンプルで簡単に実装できそうだったのでやってみました。

example

canvasタイルで生成された地図の上に、もう一つの透明なcanvasタイル地図を載せて、ピクセルカラーの値(RGBA)をオブジェクト判別用のIDとして使っています。
クリックされた座標のピクセルカラー値を取得し、元データのIDと比較することでクリックされたオブジェクトを見つけ出すという、シンプルですが色々応用が効きそうなテクニックです、
(3Dのイベントハンドリングでもこのテクニックが使われているそうです)