> ## Documentation Index
> Fetch the complete documentation index at: https://private-7c7dfe99-mintlify-fbfa8bee.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

> Mapbox Vector Tiles のエンコードに関するドキュメント

# Mapbox Vector Tiles エンコード用の関数

<div id="overview">
  ## 概要
</div>

[Mapbox Vector Tiles](https://github.com/mapbox/vector-tile-spec) (MVT) は、MapLibre や Mapbox GL などの Web マップ
クライアントがネイティブに描画する、protobuf エンコードされたタイルです。ClickHouse では、このようなタイルを SQL だけで、
連携して動作する 2 つの関数を使って完全に構築できます。

* `MVTEncodeGeom` — ジオメトリをスリッピーマップ タイル内のローカルなピクセル空間に投影し、
  タイルの範囲にクリップするスカラー関数です。
* `MVTEncode` — グループ内の投影済みジオメトリを集約し、単一レイヤーのタイルのバイナリ バイト列に変換する
  集約関数です。

2 つの補助関数 `MVTBoundingBox` と `MVTBoundingBoxMercator` はタイルのバウンディング ボックスを返すため、行を
`WHERE` 句でその範囲内に絞り込み、索引を使用できます。

Point、line、polygon のジオメトリがサポートされており、`Geometry` 型と具体的な Geo 型 (`Point`,
`LineString`, `MultiLineString`, `Ring`, `Polygon`, `MultiPolygon`) も含まれます。

結果として得られるバイト列は完全なタイルであり、`FORMAT RawBLOB` を使って HTTPインターフェイス 経由で直接返すことができます。

これらの関数は PostGIS のワークフローに対応しており、別名として PostGIS 名でも利用できます。`MVTEncodeGeom` の別名が `ST_AsMVTGeom`、
`MVTEncode` の別名が `ST_AsMVT` です。

<div id="mvtencodegeom">
  ## MVTEncodeGeom
</div>

地理座標 (経度/緯度) で与えられたジオメトリを、`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` に相当します。

出力ジオメトリの型は入力に応じて決まります。`Point` は `Point` を返します。`LineString` または `MultiLineString` は
`MultiLineString` を返します。`Ring`、`Polygon`、`MultiPolygon` は `MultiPolygon` を返します (クリッピングによりジオメトリが
複数のパーツに分割される場合があります) 。

**構文**

```sql theme={null}
MVTEncodeGeom(geometry, zoom, tile_x, tile_y[, extent[, buffer[, clip]]])
```

**引数**

* `geometry` — 経度/緯度 (度単位) の Geometry。経度は `[-180, 180]` に、緯度は Web Mercator の範囲 `[-85.05112878, 85.05112878]` に収まるように丸められます。[`Point`](/ja/reference/data-types/geo) / [`LineString`](/ja/reference/data-types/geo) / [`MultiLineString`](/ja/reference/data-types/geo) / [`Ring`](/ja/reference/data-types/geo) / [`Polygon`](/ja/reference/data-types/geo) / [`MultiPolygon`](/ja/reference/data-types/geo) / [`Geometry`](/ja/reference/data-types/geo)。
* `zoom` — Slippy-map のズームレベル。範囲は `[0, 32]` です。[`UInt8`](/ja/reference/data-types/int-uint)。
* `tile_x` — タイルの列インデックス。範囲は `[0, 2^zoom - 1]` です。[`UInt32`](/ja/reference/data-types/int-uint)。
* `tile_y` — タイルの行インデックス。範囲は `[0, 2^zoom - 1]` です。[`UInt32`](/ja/reference/data-types/int-uint)。
* `extent` — 省略可能なタイル extent (1 辺あたりのピクセル数) 。範囲は `[1, 2147483647]` です。既定値は Mapbox Vector Tile の既定値である `4096` です。[`UInt32`](/ja/reference/data-types/int-uint)。
* `buffer` — 省略可能なクリップバッファ (ピクセル単位) 。範囲は `[0, 2147483647]` です。既定値は `1` です。[`UInt32`](/ja/reference/data-types/int-uint)。
* `clip` — 省略可能なフラグです。0 以外 (既定値) の場合、ジオメトリはタイルとバッファを含む範囲にクリップされます。[`UInt8`](/ja/reference/data-types/int-uint)。

**戻り値**

タイル空間のジオメトリ、または完全にクリップされる場合は `NULL` を返します。[`Geometry`](/ja/reference/data-types/geo)。

**例**

```sql theme={null}
SELECT MVTEncodeGeom((13.37, 52.52)::Point, 10, 550, 335) AS pixel
```

```text theme={null}
┌─pixel──────┐
│ (124,3384) │
└────────────┘
```

<div id="mvtencode">
  ## MVTEncode
</div>

複数のフィーチャをバイナリの Mapbox Vector Tile レイヤーにエンコードします。これはスカラー関数 `MVTEncodeGeom` に対応する集約関数です。各入力行は 1 つのフィーチャになり、Point、line、Polygon のジオメトリをサポートします。

`geometry` 引数はタイル空間座標の `Geometry` で、通常は `MVTEncodeGeom` によって生成されます。ジオメトリが `NULL` の行 (たとえば `MVTEncodeGeom` によってクリップアウトされたもの) はスキップされます。省略可能な `properties` 引数は名前付きタプルで、その要素名がフィーチャの attribute キーとなり、要素の型がベクタータイルの値の型を決定します。

結果は単一レイヤーのタイルの生バイト列です。空のグループからは空のタイルが生成されます。これは PostGIS の `ST_AsMVT` に相当します。

**構文**

```sql theme={null}
MVTEncode(layer_name[, extent[, feature_id_name[, stringify_unsupported]]])(geometry[, properties])
```

**パラメータ**

* `layer_name` — ベクタータイルレイヤーの名前。[`String`](/ja/reference/data-types/string)。
* `extent` — タイルの各辺のピクセル数で表した extent。範囲は `[1, 2147483647]` です。デフォルトは `4096`。[`UInt32`](/ja/reference/data-types/int-uint)。
* `feature_id_name` — `properties` タプル内の符号なし整数要素の名前を指定する省略可能なパラメータです。指定した要素は、タグではなく MVT Feature の `id` (`UInt64`) として出力されます。符号付き整数は受け付けられません。`NULL` の `id` はそのフィーチャでは省略されます。パラメータは位置指定のため、これを使うには `extent` を指定する必要があります。[`String`](/ja/reference/data-types/string)。
* `stringify_unsupported` — 省略可能なフラグ (`0`/`1`、デフォルトは `0`) 。`1` を指定すると、直接サポートされていないプロパティ型 (例: 大きな整数、`UUID`、`Decimal`) は、error を発生させる代わりにテキストの `string_value` としてエンコードされます。[`UInt8`](/ja/reference/data-types/int-uint)。

**引数**

* `geometry` — タイル空間のジオメトリ。たとえば `MVTEncodeGeom` の出力です。[`Geometry`](/ja/reference/data-types/geo)。
* `properties` — フィーチャの属性を表す省略可能な named tuple。要素名は属性キーになります。[`Tuple`](/ja/reference/data-types/tuple)。

**戻り値**

単一レイヤーの Mapbox Vector Tile のバイナリ内容を返します。[`String`](/ja/reference/data-types/string)。

<div id="property-types">
  ### プロパティ型
</div>

各プロパティ要素は、その 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` としてエンコードされます。

同じプロパティ値はレイヤーの共有値プールにインターンされるため、多数のフィーチャで現れる値でも
一度だけ格納されます。

<div id="naming-the-properties-tuple">
  ### プロパティ タプルへの命名
</div>

プロパティ タプルでは、要素名を明示的に指定する必要があります。`tuple(...)` 内のカラムの別名はタプルの
要素名に**引き継がれない**ため、キャストを使って要素に名前を付けてください:

```sql theme={null}
tuple(count(), any(id))::Tuple(cluster_count UInt64, id String)
```

<div id="clustering">
  ### クラスタリング
</div>

クラスタリングは関数ではなく SQL で表現します。`MVTEncodeGeom` はピクセル単位の整数に丸めるため、ピクセルジオメトリでグループ化すると重なったジオメトリはマージされます。グループはサブクエリで集約し、クラスターごとに 1 行だけを `MVTEncode` に渡します。

```sql theme={null}
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()`) を省略してください。

<div id="mvtboundingbox">
  ## MVTBoundingBox
</div>

`zoom`、`tile_x`、`tile_y` で識別される slippy-map タイルの地理的なバウンディングボックスを、度単位のタプル
`(min_lon, min_lat, max_lon, max_lat)` として返します。

これを使うと、各行で Web Mercator 投影を再計算する代わりに、`longitude`/`latitude` カラムを直接フィルタしながら、対象の行をタイル内に絞り込めます。これにより、それらのカラムの主キーや
索引を利用できます。省略可能な `margin` は、タイルサイズに対するその割合だけボックスを各辺で拡張します。`MVTEncodeGeom` のクリップバッファをカバーするには、これを
`buffer / extent` に設定してください。

**構文**

```sql theme={null}
MVTBoundingBox(zoom, tile_x, tile_y[, margin])
```

**引数**

* `zoom` — Slippy-map のズームレベル。範囲は `[0, 32]` です。[`UInt8`](/ja/reference/data-types/int-uint)。
* `tile_x` — タイルの列インデックス。範囲は `[0, 2^zoom - 1]` です。[`UInt32`](/ja/reference/data-types/int-uint)。
* `tile_y` — タイルの行インデックス。範囲は `[0, 2^zoom - 1]` です。[`UInt32`](/ja/reference/data-types/int-uint)。
* `margin` — 各辺でボックスを拡張するための、タイルサイズに対するオプションの比率です。デフォルトは `0` です。[`Float64`](/ja/reference/data-types/float)。

**戻り値**

タイルのバウンディングボックスを、度単位のタプル `(min_lon, min_lat, max_lon, max_lat)` として返します。[`Tuple(Float64, Float64, Float64, Float64)`](/ja/reference/data-types/tuple)。

**例**

```sql theme={null}
SELECT MVTBoundingBox(0, 0, 0) AS bbox
```

```text theme={null}
┌─bbox────────────────────────────────────────────┐
│ (-180,-85.05112877980659,180,85.05112877980659)  │
└──────────────────────────────────────────────────┘
```

<div id="mvtboundingboxmercator">
  ## MVTBoundingBoxMercator
</div>

`MVTBoundingBox` の Web Mercator 版です。`MVTEncodeGeom` が内部で使用する full-`UInt32` の Web Mercator 座標空間におけるタイルの
バウンディングボックスを、タプル
`(min_x, min_y, max_x, max_y)` として返します。y 軸は下向きに増加します (上が北です) 。`longitude`/`latitude` の代わりに、Mercator 座標のカラムをマテリアライズして
それらに索引を作成するテーブルを想定しています。

**構文**

```sql theme={null}
MVTBoundingBoxMercator(zoom, tile_x, tile_y[, margin])
```

**引数**

[`MVTBoundingBox`](#mvtboundingbox) と同じです。

**戻り値**

タイルのバウンディングボックスを、Web Mercator 座標系でのタプル `(min_x, min_y, max_x, max_y)` として返します。[`Tuple(Float64, Float64, Float64, Float64)`](/ja/reference/data-types/tuple)。

**例**

```sql theme={null}
SELECT MVTBoundingBoxMercator(1, 0, 0) AS bbox
```

```text theme={null}
┌─bbox────────────────────────┐
│ (0,0,2147483648,2147483648)  │
└──────────────────────────────┘
```

<div id="restricting-rows-to-a-tile">
  ## 行をタイルに制限する
</div>

タイルには、それに属するジオメトリだけを含める必要があります。これは、相互に補完し合う 2 つのステップで表すのが最適です。つまり、`WHERE` 句で低コストかつ索引を利用するバウンディングボックス条件 (性能) と、`MVTEncodeGeom` によるクリップ処理 (正確性) です。
このクリップ処理ではタイル外のジオメトリが除外されるため、バウンディングボックス条件が多少緩くても、タイル外のジオメトリが結果に入り込むことはありません。

```sql theme={null}
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` に渡します。

<div id="serving-tiles-over-http">
  ## HTTP 経由でタイルを配信する
</div>

ClickHouse はデフォルトではタイル用のエンドポイントを公開していません。HTTP インターフェイスは `/` でのみクエリを受け付けます。すっきりした
`/tile/{z}/{x}/{y}` URL は、サーバー設定の [predefined query ハンドラー](/ja/concepts/features/interfaces/http) を使ってオペレーターが追加します。ハンドラーの `url` は `regex:` 形式を使ってパスセグメントを取り込み、それらをクエリ
パラメータにバインドし、`FORMAT RawBLOB` でバイト列を返します。

もっとも単純なケースでは、テーブルに `Geometry` カラムがあり、ハンドラーは 1 行につき 1 つの地物を返します。`MVTEncodeGeom`
は各ジオメトリを要求されたタイルに投影してクリップするため、タイル外の行は自動的に除外されます。

```xml theme={null}
<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](#clustering) および
[Restricting rows to a tile](#restricting-rows-to-a-tile) に示すように内部クエリを拡張してください。

<div id="limitations">
  ## 制限事項
</div>

* Web Mercator 投影では緯度は `±85.05112878°` に制限され、反子午線をまたぐ入力は処理できません。

* **Polygon のクリッピングでは、MVT として有効な出力は保証されません。** クリッピングによってリングの向きと閉合は修正されますが、自己交差は修正されません。したがって、自己交差する ("bow-tie" 型の) リングは修復されず、タイルとの交わり方によっては、そのまま出力される (この場合も無効なまま) か、`NULL` として破棄されます。たとえば、タイル内に完全に収まる bow-tie は破棄されますが、同じ 4 つの頂点を単純なリングとして結んだものは保持されます。

```sql theme={null}
-- self-intersecting ring -> dropped (NULL)
SELECT MVTEncodeGeom([[(40.0, 40.0), (50.0, 50.0), (50.0, 40.0), (40.0, 50.0), (40.0, 40.0)]]::Polygon, 2, 2, 1) IS NULL;  -- 1
-- simple ring, same four corners -> kept
SELECT MVTEncodeGeom([[(40.0, 40.0), (50.0, 40.0), (50.0, 50.0), (40.0, 50.0), (40.0, 40.0)]]::Polygon, 2, 2, 1) IS NULL;  -- 0
```

* **ジオメトリは、整数ピクセルグリッドに丸められる前にクリップされます。** PostGIS では、まずジオメトリを整数ピクセルグリッドにスナップし、その後でクリップします。一方、`MVTEncodeGeom` では、まずクリップし (浮動小数点の投影座標上で実行) 、その後で丸めます。タイルの端付近では、この違いにより、本来なら境界ピクセルに丸められるはずの座標が落とされることがあります。たとえば、`buffer = 0` の場合、タイル境界のすぐ東にある点はクリップされます。これは、先に丸める方式であれば保持される境界ピクセル `4096` に丸められるにもかかわらずです:

```sql theme={null}
-- floating-point x ~= 4096.23 is just past the east edge (extent = 4096) -> clipped
SELECT MVTEncodeGeom((90.005, 30.0)::Point, 2, 2, 1, 4096, 0) IS NULL;          -- 1
-- the same point projected without clipping rounds onto the edge pixel:
SELECT MVTEncodeGeom((90.005, 30.0)::Point, 2, 2, 1, 4096, 0, false);           -- (4096,2664)
```
