GanosBase Geometry Pyramid は、PolarDB for PostgreSQL 向けの時空間拡張機能です。ジオメトリ列を含むテーブル上にベクタータイルピラミッドを構築し、クライアント側での高速レンダリングを実現するための効率的なタイルクエリを可能にします。
8 コアの PolarDB for PostgreSQL クラスターにおいて、7,000 万件の建物フットプリントレコードからベクタータイルピラミッドを構築するには約 6 分かかります。スパースなピラミッド構造により、高密度領域のみ事前にタイルを生成することでストレージコストを削減しています — タイル内に 2 個未満のジオメトリオブジェクトが含まれる場合、そのタイルはオンデマンドで生成されます。
基本概念
ベクタータイリング とは、ベクタージオメトリデータ(ポイント、ライン、ポリゴン)をフロントエンドアプリケーションが個別にスタイル設定およびレンダリング可能なタイルに変換する処理です。タイリング仕様は「何を表示するか」を定義し、クライアント側が「どのように表示するか」を決定します。
Mapbox Vector Tile(MVT) は、ベクタータイルデータの保存および転送に用いられる標準的なバイナリ形式です。MVT ファイルには名前付きレイヤーが含まれ、各レイヤーはジオメトリ特徴量(MoveTo、LineTo、ClosePath コマンドでエンコード)およびキー・バリュー形式の属性メタデータを保持します。ほとんどの最新の GIS アプリケーションおよび Web マッピングライブラリは、MVT をネイティブでサポートしています。
動的ベクタータイリング は、リクエストに応じてタイルをオンデマンドで生成する方式です。可視化リクエストが到着すると、データベースはタイル範囲内のジオメトリを取得するための空間クエリを実行し、ST_AsMVTGeom で座標変換を行い、ST_AsMVT で結果をエンコードします。GanosBase では、このパイプラインを高速化するための 3 つの関数を提供しています:
| ステージ | 関数 | 機能概要 |
|---|---|---|
| サンプリング | ST_IsRandomSampled | ジオメトリオブジェクトがランダムサンプリング済みかどうかを確認し、座標変換前の段階で早期フィルタリングを可能にします |
| 座標変換 | ST_AsMVTGeomEx | ジオメトリを変換し、サブピクセルレベルで描画されてしまうオブジェクトを除外します |
| エンコーディング | ST_AsMVTEx | ジオメトリを MVT 形式でエンコードし、可視化後に視覚的に無意味なオブジェクトを除外します |
これらの関数のいずれかを適用することで、動的ベクタータイリングの処理速度を向上させることができます。
プリタイリング は、タイルをバッチ処理で事前に生成・保存し、必要に応じてストレージから配信する方式です。
スパースピラミッド は、GanosBase Geometry Pyramid が採用するアーキテクチャです。これは、上記の両方式を統合したものです:高密度領域(2 個以上のジオメトリオブジェクトを含む)のタイルは事前に生成・保存され、低密度領域のタイルはオンデマンドで生成されます。以下の図はその構造を示しており、塗りつぶされたタイルは事前生成済み、灰色のタイルは動的生成となります。

レンダリング とは、フロントエンドソフトウェアが受信したベクタータイルデータに基づいて画像を作成するプロセスを指します。従来、レンダリングはフロントエンドソフトウェアによって完了されていましたが、ベクタータイルピラミッドモデルでは、データベース側でもレンダリングが可能です:データベースがマップをレンダリングし、得られた画像をフロントエンドソフトウェアへ送信して表示します。
適用範囲/利用シーン/ユースケース
ジオメトリオブジェクトは、道路、建物、または関心のある地点(POI)といった現実世界の地物を表します。ベクタータイルピラミッドは、OGC のすべてのジオメトリタイプ(ポイント、ラインストリング、ポリゴン)をサポートします:
POI および軌道ポイント(ポイント): 異なるズームレベルにわたって、地点または移動体の分布および密度を可視化します。
道路および水路(ラインストリング): ルート計画および交通分析のために、道路および水路ネットワークを動的にクエリします。

建物、河川、森林(ポリゴン): 都市計画および環境モニタリングを支援するために、土地利用パターンを可視化します。

ベクタータイルピラミッドの選択基準
データ量および更新頻度に応じて、ベクタータイルピラミッドと動的ベクタータイリングのどちらを採用するかを判断してください:
| 動的ベクタータイリング | ベクタータイルピラミッド | |
|---|---|---|
| 前処理 | 最小限 — 初期設定不要 | 必須 — 最初のクエリ実行前にピラミッドを構築する必要があります |
| クエリ遅延 | やや高め — ジオメトリの取得および処理をリクエストごとに実行 | 低め — 事前構築済みのタイルを直接配信 |
| ストレージオーバーヘッド | なし | スパース:高密度タイルのみを保存 |
| データ更新 | 追加操作不要 | ST_UpdatePyramid(小規模な空間範囲)を実行するか、または再構築(大規模な空間範囲)を行います |
| 推奨用途 | 更新頻度が高い、またはクエリ量が中程度のデータセット | 数千万~数億件のレコード、読み取り中心のワークロード |
パフォーマンス要件を満たす場合は、動的ベクタータイリングをご利用ください。一方、クエリ遅延が重要な要件となる数千万件規模のデータセットでは、ベクタータイルピラミッドへの切り替えを検討してください。
クイックスタート
前提条件
開始する前に、以下の条件を満たしていることを確認してください:
PolarDB for PostgreSQL クラスター
ジオメトリ列を含むテーブル
ジオメトリ列に対する空間インデックス
拡張機能のインストール
CREATE EXTENSION ganos_geometry_pyramid CASCADE;権限関連の問題を回避するため、拡張機能は public スキーマにインストールすることを推奨します。
CREATE EXTENSION ganos_geometry_pyramid WITH SCHEMA public;ベクタータイルピラミッドの構築
-- "test" テーブル上にピラミッドを構築
-- id 列は int4 または int8 型である必要があります
-- sourceSRS はソースデータの座標系を指定します(ここでは EPSG:4326)
SELECT ST_BuildPyramid('test', 'geom', 'id', '{"sourceSRS":4326}');構築が完了すると、ピラミッドはクエリにすぐに利用可能になります。
タイルのクエリ
タイルは z/x/y スキームでアドレス指定されます。z はズームレベル、x は列インデックス、y は行インデックスです。すべての値はゼロベースであり、タイルグリッドの左上を原点とします。ズームレベル 0 では世界全体が 1 枚のタイルでカバーされ、以降の各レベルではタイル数が 4 倍になります。API では、このアドレスは z_x_y 形式で表現されます。
ST_Tile を使用すると MVT 形式のベクタータイルを返し、ST_AsPNG を使用するとレンダリング済みの PNG イメージを返します。
-- ズームレベル 0、列 0、行 0 のタイルをクエリ
-- デフォルトの投影座標系は EPSG:3857 です
-- タイルが存在する場合は MVT 形式で返却し、存在しない場合は null を返却します
SELECT ST_Tile('test', '0_0_0');ピラミッドの更新
テーブルの行を更新した後は、変更内容を反映するためにピラミッドを更新してください。
空間範囲が小さい更新(全エリアの 1 % 未満)の場合:
-- 新しいジオメトリレコードを挿入
INSERT INTO test(id, geom) VALUES (1, ST_GeomFromEWKT('SRID=4326;POINT(10.1 10.1)'));
INSERT INTO test(id, geom) VALUES (2, ST_GeomFromEWKT('SRID=4326;LINESTRING(10.1 10.1,11 11)'));
INSERT INTO test(id, geom) VALUES (3, ST_GeomFromEWKT('SRID=4326;POLYGON((10 10,11 11,11 12,10 10))'));
-- バウンディングボックスを用いて影響を受ける領域のみを更新
SELECT ST_UpdatePyramid('test', 'geom', 'id', ST_SetSRID(ST_MakeBox2D(ST_Point(9, 9), ST_Point(12, 12)), 4326));データセットの大部分に影響を与える更新の場合、ピラミッドを再構築してください:
-- 異なる地理的エリアにまたがるレコードを挿入
INSERT INTO test(id, geom) VALUES (4, ST_GeomFromEWKT('SRID=4326;POINT(-59 -45)'));
INSERT INTO test(id, geom) VALUES (5, ST_GeomFromEWKT('SRID=4326;LINESTRING(110 60,115 70)'));
INSERT INTO test(id, geom) VALUES (6, ST_GeomFromEWKT('SRID=4326;POLYGON((-120 59,-110 65,-110 70,-120 59))'));
-- ピラミッドを再構築(既存のピラミッドは自動的に削除されます)
SELECT ST_BuildPyramid('test', 'geom', 'id', '{"sourceSRS":4326}');ピラミッドおよび拡張機能の削除
-- ピラミッドを削除
SELECT ST_DeletePyramid('test');
-- 拡張機能を削除
DROP EXTENSION ganos_geometry_pyramid CASCADE;高度な構成
ピラミッドへの名前付け
デフォルトでは、ピラミッドは対応するテーブル名と同じ名前になります。同一テーブル上に複数のピラミッドを作成する場合は、カスタム名を指定してください:
-- "test" テーブル上に "hello" という名前のピラミッドを作成
SELECT ST_BuildPyramid('test', 'geom', 'id', '{"name": "hello"}');並列タスクによる構築
大規模なデータセットでは、並列タスクを活用することでピラミッド構築を高速化できます。0 を指定すると、許容される最大並列数が自動的に使用されます。
-- 4 つの並列タスクを使用して構築
SELECT ST_BuildPyramid('test', 'geom', 'id', '{"parallel": 4}');制約事項:
並列数は、設定済みコア数の 4 倍を超えてはなりません。
並列構築では 2 相コミット機構が使用されます。PostgreSQL の設定ファイルで
max_prepared_transactionsを100以上に設定し、クラスターを再起動してください。
タイルパラメーターの設定
| パラメーター | 制約事項 | 調整タイミング |
|---|---|---|
tileSize | 256 の倍数であること。最大値は 4096 | 大規模データセットからフルマップを可視化する際にレンダリングの並列性を高めるために、512 に設定します |
tileExtend | 0~256 | エッジ上の特徴量を含むためのタイルバッファーを制御するために調整します |
-- タイルサイズを 512、タイル拡張量を 8 に設定
SELECT ST_BuildPyramid('test', 'geom', 'id', '{
"tileSize": 512,
"tileExtend": 8
}');最大ピラミッドレベルの設定
最大レベルは、事前に構築されるズームレベル数を制御します。このレベルを超えるズームレベルでは、タイルが動的に生成されます。設定しない場合、システムはデータ密度に基づいて最大レベルを算出し、デフォルト値は 16 です。
-- ズームレベル 0~12 を事前に構築し、それより高いレベルは動的に生成
SELECT ST_BuildPyramid('test', 'geom', 'id', '{"maxLevel": 12}');buildRules を用いたレベルごとのフィルタリング
buildRules を使用すると、各ズームレベルで表示される特徴量およびタイルに格納される属性を制御できます。これにより、粗いズームレベルにおけるタイルサイズを削減でき、最上位のピラミッドレベルのタイル生成(時間のかかる処理)を回避できます。
-- レベル 0~5:空のタイル("1!=1" というフィルターは常に偽)
-- レベル 6~9:code=1 の特徴量のみ、"name" 属性を含む
-- レベル 10~15:すべての特徴量、"name" および "width" 属性を含む
SELECT ST_BuildPyramid('test', 'geom', 'id', '{
"buildRules":[
{
"level":[0,1,2,3,4,5],
"value": {
"filter": "1!=1"
}
},
{
"level":[6,7,8,9],
"value": {
"filter": "code=1",
"attrFields": ["name"]
}
},
{
"level":[10,11,12,13,14,15],
"value": {
"attrFields": ["name", "width"]
}
}
]
}');特徴量のマージ
merge を buildRules 内で使用すると、同じ属性値を持つ隣接する特徴量を結合できます。これにより、粗いズームレベルにおける特徴量の総数を削減できます。
-- レベル 0~5 で、code=1 または code=2 の特徴量をマージ
SELECT ST_BuildPyramid('test', 'geom', 'id', '{
"buildRules":[
{
"level":[0,1,2,3,4,5],
"value": {
"merge": ["code=1","code=2"]
}
}
]
}');ST_BuildPyramidUseGeomSideLen を用いた高速な構築および更新
ST_BuildPyramidUseGeomSideLen は、ST_BuildPyramid と比較して、構築および増分更新の両方において効率性が向上しています。ただし、各ジオメトリの最大バウンディングボックス幅(X 軸または Y 軸方向)を格納する追加列と、その列に対する B ツリーインデックスが必要です。
-- ステップ 1:ジオメトリの最大バウンディングボックス幅を格納する列を追加
ALTER TABLE test
ADD COLUMN geom_side_len DOUBLE PRECISION;
-- ステップ 2:列を埋める
CREATE OR REPLACE FUNCTION add_max_len_values() RETURNS VOID AS $$
DECLARE
t_curs CURSOR FOR
SELECT * FROM test;
t_row test%ROWTYPE;
gm GEOMETRY;
x_min DOUBLE PRECISION;
x_max DOUBLE PRECISION;
y_min DOUBLE PRECISION;
y_max DOUBLE PRECISION;
BEGIN
FOR t_row IN t_curs LOOP
SELECT t_row.geom INTO gm;
SELECT ST_XMin(gm) INTO x_min;
SELECT ST_XMax(gm) INTO x_max;
SELECT ST_YMin(gm) INTO y_min;
SELECT ST_YMax(gm) INTO y_max;
UPDATE test
SET geom_side_len = GREATEST(x_max - x_min, y_max - y_min)
WHERE CURRENT OF t_curs;
END LOOP;
END;
$$ LANGUAGE plpgsql;
SELECT add_max_len_values();
-- ステップ 3:列に対する B ツリーインデックスを作成
CREATE INDEX ON test USING btree(geom_side_len);
-- ステップ 4:幅を格納する列を用いてピラミッドを構築
SELECT ST_BuildPyramidUseGeomSideLen('roads', 'geom', 'geom_side_len', 'id',
'{"sourceSRS":4326}');