MapboxVectorTileとプロトコルバッファ
@Kanahiro
MapboxVectorTileとは?
プロトコルバッファとは?
今回やってみること
PBFをデコードする
import * as protobuf from 'protobufjs';
const buffer = fs.readFileSync('./13-7096-5010.pbf');
// buffer = <Buffer 1f 8b 08 ... >
const root = await protobuf.load(path.join(__dirname, 'vector_tile.proto'));
const protoVectorTile = root.lookupType('tile');
const vectorTile = protoVectorTile.decode(buffer);
const vectorTileObject = protoVectorTile.toObject(vectorTile);
/* vectorTileObject
{
layers: [
{
name: 'water',
features: [Array],
keys: [Array],
values: [Array],
extent: 4096,
version: 2
},
// 以下略
]
}
*/
PBFにエンコードする
import * as protobuf from 'protobufjs';
/* vectorTileObject
{
layers: [
{
name: 'water',
features: [Array],
keys: [Array],
values: [Array],
extent: 4096,
version: 2
},
// 以下略
]
}
*/
const root = await protobuf.load(path.join(__dirname, 'vector_tile.proto'));
const protoVectorTile = root.lookupType('tile');
const vectorTile = protoVectorTile.create(vectorTileObject);
const buffer = protoVectorTile.encode(vectorTile).finish();
// buffer = <Buffer 1f 8b 08 ... >
PBFの取り扱いまとめ
VectorTileのデータ構造①
VectorTileのデータ構造②
interface VectorTileObject {
layers: VectorTileLayer[];
}
interface VectorTileLayer {
name: string;
keys: string[];
values?: Value[];
extent: number;
version: number;
features: VectorTileLayerFeature[];
}
interface VectorTileLayerFeature {
tags?: number[];
type: number;
geometry: number[];
}
VectorTileのデータ構造③
{
tags: [ 0, 0 ],
type: 3,
geometry: [
9, 8320, 127, 26, 0, 8448, 8447, 0, 0, 8447, 15, 9,
6432, 6542, 114, 1, 4, 3, 1, 1, 2, 4, 6, 15,
3, 1, 4, 6, 8, 8, 6, 10, 2, 8, 0, 2,
7, 5, 3, 4, 3, 3, 5, 15, 9, 89, 73, 258,
4, 8, 5, 2, 5, 1, 2, 4, 3, 2, 3, 10,
6, 6, 0, 8, 4, 4, 6, 0, 6, 6, 6, 2,
6, 9, 4, 4, 6, 1, 0, 5, 2, 2, 2, 18,
4, 2, 6, 8, 4, 0, 3, 13, 4, 3, 5, 11,
0, 5, 5, 2,
... 2339 more items
]
}
{
"type": "Feature",
"properties": { "mvt_id": null, "class": "ocean", "intermittent": null },
"geometry": {
"type": "MultiPolygon",
"coordinates": [
[
[
[131.835937499999972, -37.265309955618747],
[131.835937499999972, -37.230328387603876],
[131.879882812499972, -37.230328387603876],
// 中略
[131.863253116607638, -37.250408904966733],
[131.863220930099487, -37.250383284576458]
]
]
]
}
}
VectorTileのデータ構造④
{
tags: [ 0, 0 ],
type: 3,
geometry: [
9, 8320, 127, 26, 0, 8448, 8447, 0, 0, 8447, 15, 9,
6432, 6542, 114, 1, 4, 3, 1, 1, 2, 4, 6, 15,
3, 1, 4, 6, 8, 8, 6, 10, 2, 8, 0, 2,
7, 5, 3, 4, 3, 3, 5, 15, 9, 89, 73, 258,
4, 8, 5, 2, 5, 1, 2, 4, 3, 2, 3, 10,
6, 6, 0, 8, 4, 4, 6, 0, 6, 6, 6, 2,
6, 9, 4, 4, 6, 1, 0, 5, 2, 2, 2, 18,
4, 2, 6, 8, 4, 0, 3, 13, 4, 3, 5, 11,
0, 5, 5, 2,
... 2339 more items
]
}
VectorTileのデータ構造⑤
{
name: 'water',
features: [
{ tags: [Array], type: 3, geometry: [Array] },
{ tags: [Array], type: 3, geometry: [Array] },
{ tags: [Array], type: 3, geometry: [Array] }
],
keys: [ 'class', 'intermittent', 'brunnel' ],
values: [
{ stringValue: 'ocean' },
{ stringValue: 'lake' },
{ uintValue: [Long] }
],
extent: 4096,
version: 2
}
左はVectorTile-Layerのデータ構造
feature-propertiesの復元の鍵は、keysとvalues
たとえばtags: [ 0, 0, 0, 1 ]は以下のように復元できる(stringValueとかは一旦無視)
{ keys[0] : values[0] } -> { class: ‘ocean’ }
{ keys[0] : values[1] } -> { class: ‘lake’ }
tagsの偶数番の値は、keysのインデックスを示す
tagsの奇数番の値は、valuesのインデックスを示す
※なのでtagsの配列長は常に偶数
VectorTile-Featureの属性を編集したければ、layer, featureの関係に注意する必要がある
とくに、属性のカラムや値が既出でない場合は、layersにまず値を”登録”してから、それに応じたtagsを追加しなければならない
VectorTileのデータ構造⑥
Q.なんでそんなめんどくさいデータの持ち方してるの?
A.データを効率的に保持するためです。例えばレイヤーが10個あり、それぞれ地物を10個持つとします。その全てに’class’というカラムがあるとすると、100回’class’という文字列が登場します。これは効率が悪いです。’class’=0と置き換えを一度定義すれば、100回出てくるのは0という数値だけになります。
実際に編集してみた
実コードはかなり長いのでチラ見せ、結果は以下
タイル内の全ての地物に、{test: ‘test’}という属性を追加してみた
感想