概要
Mapbox Vector Tiles (MVT) は、MapLibre や Mapbox GL などの Web マップ クライアントがネイティブに描画する、protobuf エンコードされたタイルです。ClickHouse では、このようなタイルを SQL だけで、 連携して動作する 2 つの関数を使って完全に構築できます。
MVTEncodeGeom— ジオメトリをスリッピーマップ タイル内のローカルなピクセル空間に投影し、 タイルの範囲にクリップするスカラー関数です。MVTEncode— グループ内の投影済みジオメトリを集約し、単一レイヤーのタイルのバイナリ バイト列に変換する 集約関数です。
2 つの補助関数 MVTBoundingBox と MVTBoundingBoxMercator はタイルのバウンディング ボックスを返すため、行を
WHERE 句でその範囲内に絞り込み、索引を使用できます。
Point、line、polygon のジオメトリがサポートされており、Geometry 型と具体的な Geo 型 (Point,
MultiPoint, LineString, MultiLineString, Ring, Polygon, MultiPolygon) も含まれます。
結果として得られるバイト列は完全なタイルであり、FORMAT RawBLOB を使って HTTPインターフェイス 経由で直接返すことができます。
これらの関数は PostGIS のワークフローに対応しており、別名として PostGIS 名でも利用できます。MVTEncodeGeom の別名が ST_AsMVTGeom、
MVTEncode の別名が ST_AsMVT です。
MVTEncodeGeom
地理座標 (経度/緯度) で与えられたジオメトリを、zoom、tile_x、tile_y で識別される
slippy-map タイルのタイル内ローカルなピクセル空間に投影し、整数ピクセルグリッドにスナップしてタイルにクリップし、
タイル空間のジオメトリを返します。
投影には、UInt32 の座標範囲全体にわたる Web Mercator を使用します。返される座標の始点はタイルの
左上隅で、y 軸は下向きです。これは Mapbox Vector
Tile フォーマットの座標規約であるため、結果はそのまま MVTEncode に渡せます。座標は整数ピクセルに丸められるため、
MVTEncodeGeom でグループ化すると、同じグリッド上にあるジオメトリは 1 つのクラスターに集約されます。
clip が有効な場合 (デフォルト) 、ジオメトリは buffer ピクセルだけ拡張されたタイル (各軸で
[-buffer, extent + buffer] の範囲) にクリップされます。完全に外側にあるジオメトリは NULL になります。これは
PostGIS の ST_AsMVTGeom に相当します。
Polygon の座標は、検証前に 2^30 の window に制限されます。これは zoom 18、extent 4096 における
世界全体のピクセル幅にちょうど相当します。そのため、現実的なタイルではジオメトリは検証されますがクリップされることはなく、
この制限が影響するのは zoom または extent が極端な値の場合だけです。
出力ジオメトリの型は入力に応じて決まります。Point は Point を返します。MultiPoint は MultiPoint を返します。LineString または MultiLineString は
MultiLineString を返します。Ring、Polygon、MultiPolygon は MultiPolygon を返します (クリッピングによりジオメトリが
複数のパーツに分割される場合があります) 。
構文
MVTEncodeGeom(geometry, zoom, tile_x, tile_y[, extent[, buffer[, clip]]])引数
geometry— 経度/緯度 (度単位) の Geometry。経度は[-180, 180]に、緯度は Web Mercator の範囲[-85.05112878, 85.05112878]に収まるように丸められます。Point/MultiPoint/LineString/MultiLineString/Ring/Polygon/MultiPolygon/Geometry。zoom— Slippy-map のズームレベル。範囲は[0, 32]です。UInt8。tile_x— タイルの列インデックス。範囲は[0, 2^zoom - 1]です。UInt32。tile_y— タイルの行インデックス。範囲は[0, 2^zoom - 1]です。UInt32。extent— 省略可能なタイル extent (1 辺あたりのピクセル数) 。範囲は[1, 2147483647]です。既定値は Mapbox Vector Tile の既定値である4096です。UInt32。buffer— 省略可能なクリップバッファ (ピクセル単位) 。範囲は[0, 2147483647]です。既定値は1です。UInt32。clip— 省略可能なフラグです。0 以外 (既定値) の場合、ジオメトリはタイルとバッファを含む範囲にクリップされます。UInt8。
戻り値
タイル空間のジオメトリ、または完全にクリップされる場合は NULL を返します。Geometry。
例
SELECT MVTEncodeGeom((13.37, 52.52)::Point, 10, 550, 335) AS pixel┌─pixel──────┐
│ (124,3384) │
└────────────┘MVTEncode
複数のフィーチャをバイナリの Mapbox Vector Tile レイヤーにエンコードします。これはスカラー関数 MVTEncodeGeom に対応する集約関数です。各入力行は 1 つのフィーチャになり、Point、line、Polygon のジオメトリをサポートします。
geometry 引数はタイル空間座標の Geometry で、通常は MVTEncodeGeom によって生成されます。ジオメトリが NULL の行 (たとえば MVTEncodeGeom によってクリップアウトされたもの) はスキップされます。省略可能な properties 引数は名前付きタプルで、その要素名がフィーチャの attribute キーとなり、要素の型がベクタータイルの値の型を決定します。
結果は単一レイヤーのタイルの生バイト列です。空のグループからは空のタイルが生成されます。これは PostGIS の ST_AsMVT に相当します。
構文
MVTEncode(layer_name[, extent[, feature_id_name[, stringify_unsupported]]])(geometry[, properties])パラメータ
layer_name— ベクタータイルレイヤーの名前。String。extent— タイルの各辺のピクセル数で表した extent。範囲は[1, 2147483647]です。デフォルトは4096。UInt32。feature_id_name—propertiesタプル内の符号なし整数要素の名前を指定する省略可能なパラメータです。指定した要素は、タグではなく MVT Feature のid(UInt64) として出力されます。符号付き整数は受け付けられません。NULLのidはそのフィーチャでは省略されます。パラメータは位置指定のため、これを使うにはextentを指定する必要があります。String。stringify_unsupported— 省略可能なフラグ (0/1、デフォルトは0) 。1を指定すると、直接サポートされていないプロパティ型 (例: 大きな整数、UUID、Decimal) は、error を発生させる代わりにテキストのstring_valueとしてエンコードされます。UInt8。
引数
geometry— タイル空間のジオメトリ。たとえばMVTEncodeGeomの出力です。Geometry。properties— フィーチャの属性を表す省略可能な named tuple。要素名は属性キーになります。Tuple。
戻り値
単一レイヤーの Mapbox Vector Tile のバイナリ内容を返します。String。
プロパティ型
各プロパティ要素は、その ClickHouse 型に対応する Mapbox Vector Tile の Value バリアントとしてエンコードされます。
| ClickHouse 型 | ベクタータイルの値型 |
|---|---|
String / FixedString |
string_value |
Float32 / BFloat16 |
float_value |
Float64 |
double_value |
Bool |
bool_value |
Int8 / Int16 / Int32 / Int64 / Date32 |
sint_value |
UInt8 / UInt16 / UInt32 / UInt64 / Date / DateTime |
uint_value |
型は Nullable や LowCardinality でラップされることがあります。NULL 値の場合、そのフィーチャでは 해당 attribute は省略されます。これは、ベクタータイル形式では null を扱えないためです。その他のプロパティ型では例外が発生しますが、stringify_unsupported が設定されている場合は、テキストの string_value としてエンコードされます。
同じプロパティ値はレイヤーの共有値プールにインターンされるため、多数のフィーチャで現れる値でも 一度だけ格納されます。
プロパティ タプルへの命名
プロパティ タプルでは、要素名を明示的に指定する必要があります。tuple(...) 内のカラムの別名はタプルの
要素名に引き継がれないため、キャストを使って要素に名前を付けてください:
tuple(count(), any(id))::Tuple(cluster_count UInt64, id String)クラスタリング
クラスタリングは関数ではなく SQL で表現します。MVTEncodeGeom はピクセル単位の整数に丸めるため、ピクセルジオメトリでグループ化すると重なったジオメトリはマージされます。グループはサブクエリで集約し、クラスターごとに 1 行だけを MVTEncode に渡します。
SELECT MVTEncode('points')(geom, tuple(cluster_count)::Tuple(cluster_count UInt64)) AS tile
FROM
(
SELECT MVTEncodeGeom((lon, lat)::Point, 10, 550, 335) AS geom, count() AS cluster_count
FROM points
GROUP BY geom
)
SETTINGS allow_suspicious_types_in_group_by = 1;Geometry 値でグループ化するには、allow_suspicious_types_in_group_by = 1 が必要です。これは、Variant ベースの
Geometry 型によるグループ化がデフォルトで制限されているためです。クラスタ化されたフィーチャではなく、入力の各行につき 1 つのフィーチャを出力するには、
内側の GROUP BY (および count()) を省略してください。
MVTBoundingBox
zoom、tile_x、tile_y で識別される slippy-map タイルの地理的なバウンディングボックスを、度単位のタプル
(min_lon, min_lat, max_lon, max_lat) として返します。
これを使うと、各行で Web Mercator 投影を再計算する代わりに、longitude/latitude カラムを直接フィルタしながら、対象の行をタイル内に絞り込めます。これにより、それらのカラムの主キーや
索引を利用できます。省略可能な margin は、タイルサイズに対するその割合だけボックスを各辺で拡張します。MVTEncodeGeom のクリップバッファをカバーするには、これを
buffer / extent に設定してください。
構文
MVTBoundingBox(zoom, tile_x, tile_y[, margin])引数
zoom— Slippy-map のズームレベル。範囲は[0, 32]です。UInt8。tile_x— タイルの列インデックス。範囲は[0, 2^zoom - 1]です。UInt32。tile_y— タイルの行インデックス。範囲は[0, 2^zoom - 1]です。UInt32。margin— 各辺でボックスを拡張するための、タイルサイズに対するオプションの比率です。デフォルトは0です。Float64。
戻り値
タイルのバウンディングボックスを、度単位のタプル (min_lon, min_lat, max_lon, max_lat) として返します。Tuple(Float64, Float64, Float64, Float64)。
例
SELECT MVTBoundingBox(0, 0, 0) AS bbox┌─bbox────────────────────────────────────────────┐
│ (-180,-85.05112877980659,180,85.05112877980659) │
└──────────────────────────────────────────────────┘MVTBoundingBoxMercator
MVTBoundingBox の Web Mercator 版です。MVTEncodeGeom が内部で使用する full-UInt32 の Web Mercator 座標空間におけるタイルの
バウンディングボックスを、タプル
(min_x, min_y, max_x, max_y) として返します。y 軸は下向きに増加します (上が北です) 。longitude/latitude の代わりに、Mercator 座標のカラムをマテリアライズして
それらに索引を作成するテーブルを想定しています。
構文
MVTBoundingBoxMercator(zoom, tile_x, tile_y[, margin])引数
MVTBoundingBox と同じです。
戻り値
タイルのバウンディングボックスを、Web Mercator 座標系でのタプル (min_x, min_y, max_x, max_y) として返します。Tuple(Float64, Float64, Float64, Float64)。
例
SELECT MVTBoundingBoxMercator(1, 0, 0) AS bbox┌─bbox────────────────────────┐
│ (0,0,2147483648,2147483648) │
└──────────────────────────────┘行をタイルに制限する
タイルには、それに属するジオメトリだけを含める必要があります。これは、相互に補完し合う 2 つのステップで表すのが最適です。つまり、WHERE 句で低コストかつ索引を利用するバウンディングボックス条件 (性能) と、MVTEncodeGeom によるクリップ処理 (正確性) です。
このクリップ処理ではタイル外のジオメトリが除外されるため、バウンディングボックス条件が多少緩くても、タイル外のジオメトリが結果に入り込むことはありません。
WITH
1 AS buffer,
4096 AS extent,
MVTBoundingBox({z:UInt8}, {x:UInt32}, {y:UInt32}, buffer / extent) AS bounding_box -- margin matches the clip buffer
SELECT MVTEncode('points')(geom, tuple(cluster_count)::Tuple(cluster_count UInt64))
FROM
(
SELECT MVTEncodeGeom((lon, lat)::Point, {z:UInt8}, {x:UInt32}, {y:UInt32}) AS geom, count() AS cluster_count
FROM points
WHERE lon BETWEEN bounding_box.1 AND bounding_box.3 AND lat BETWEEN bounding_box.2 AND bounding_box.4 -- index-using prefilter
GROUP BY geom
)
SETTINGS allow_suspicious_types_in_group_by = 1バウンディングボックスのpredicateは、あくまで粗い事前filterにすぎません。正確なタイル境界は
MVTEncodeGeom のclipによって適用されます。クリッピングを無効にし、
WHERE predicateのみに依存するには、clip => false (7番目のargument) を MVTEncodeGeom に渡します。
HTTP 経由でタイルを配信する
ClickHouse はデフォルトではタイル用のエンドポイントを公開していません。HTTP インターフェイスは / でのみクエリを受け付けます。すっきりした
/tile/{z}/{x}/{y} URL は、サーバー設定の predefined query ハンドラー を使ってオペレーターが追加します。ハンドラーの url は regex: 形式を使ってパスセグメントを取り込み、それらをクエリ
パラメータにバインドし、FORMAT RawBLOB でバイト列を返します。
もっとも単純なケースでは、テーブルに Geometry カラムがあり、ハンドラーは 1 行につき 1 つの地物を返します。MVTEncodeGeom
は各ジオメトリを要求されたタイルに投影してクリップするため、タイル外の行は自動的に除外されます。
<http_handlers>
<rule>
<methods>GET</methods>
<url><![CDATA[regex:/tile/(?P<z>\d+)/(?P<x>\d+)/(?P<y>\d+)]]></url>
<handler>
<type>predefined_query_handler</type>
<query>
SELECT MVTEncode('shapes')(
MVTEncodeGeom(geom, {z:UInt8}, {x:UInt32}, {y:UInt32}),
tuple(id, name)::Tuple(id UInt32, name String))
FROM shapes
FORMAT RawBLOB
</query>
<content_type>application/vnd.mapbox-vector-tile</content_type>
</handler>
</rule>
<defaults/>
</http_handlers>ここで shapes は、geom Geometry カラム (points、lines、polygons を任意に混在できる) を持つテーブルです。GET /tile/10/550/335
は、エンコードされたタイルを返します。
ポイントデータの場合も、MVTEncodeGeom((lon, lat)::Point, …) を使ってポイントをインラインで構築すれば、通常の longitude/latitude カラムに対して同様に機能します。同一位置のフィーチャをクラスター化したり、大きなテーブルに対して索引を使用するバウンディングボックスの事前フィルタを追加したりするには、Clustering および
Restricting rows to a tile に示すように内部クエリを拡張してください。
制限事項
- Web Mercator 投影では緯度は
±85.05112878°に制限され、反子午線をまたぐ入力は処理できません。