> ## Documentation Index
> Fetch the complete documentation index at: https://clickhouse.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

> Формат ввода и вывода для документов GeoJSON FeatureCollection: при вводе создается по одной строке для каждой возможности со столбцами id, geometry и properties; при выводе — по одной возможности на строку.

# GeoJSON

| Ввод | Вывод | Псевдоним |
| ---- | ----- | --------- |
| ✔    | ✔     |           |

<div id="description">
  ## Описание
</div>

Данные [GeoJSON](https://geojson.org/) передаются в виде единого документа [`FeatureCollection`](https://datatracker.ietf.org/doc/html/rfc7946#section-3.3), который ClickHouse сопоставляет с тремя столбцами — `id`, `geometry` и `properties` — по одному набору для каждого `Feature`. [Чтение](#reading-data) документа даёт по одной строке на каждую возможность, а [запись](#writing-data) — по одной возможности на строку.

<div id="reading-data">
  ## Чтение данных
</div>

Чтение `FeatureCollection` создаёт по одной строке для каждой возможности со следующей фиксированной схемой:

| Столбец      | Тип                | Описание                                                                                                                                                                                        |
| ------------ | ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`         | `Nullable(String)` | Член `id` возможности (JSON-строка или число), сохраняемый как текст; `NULL`, если `id` отсутствует или имеет значение `null`, при этом явно заданный пустой строковый id сохраняется как `''`. |
| `geometry`   | `Geometry`         | Геометрия возможности, сохраняемая как тип варианта `Geometry`.                                                                                                                                 |
| `properties` | `Nullable(JSON)`   | Объект `properties` возможности, сохраняемый как полуструктурированный столбец `JSON`. Явно заданное `"properties": null` сохраняется как `NULL`.                                               |

Каждая геометрия хранится в типе `Geometry` ClickHouse (то есть `Variant`). Поддерживаются следующие геометрические типы GeoJSON: `Point`, `LineString`, `MultiLineString`, `Polygon` и `MultiPolygon`. Два других геометрических типа GeoJSON, `GeometryCollection` и `MultiPoint`, не могут быть представлены типом `Geometry`; чтение одного из них в столбец `geometry` по умолчанию вызывает исключение, но это поведение можно изменить так, чтобы вместо этого вставлялся `NULL` — см. [Обработка неподдерживаемых геометрических типов](#unsupported-geometry) ниже. По умолчанию столбец `geometry` имеет значение `NULL` только тогда, когда геометрия возможности является явным JSON `null`; при `input_format_geojson_unsupported_geometry_handling = 'null'` он также имеет значение `NULL` для неподдерживаемого геометрического типа.

Структура документа проверяется: `type` верхнего уровня должен быть `FeatureCollection`, а каждый элемент `features` должен иметь `type` `Feature`. По умолчанию координаты должны удовлетворять инвариантам геометрической формы в GeoJSON — `LineString` (и каждая линия в `MultiLineString`) должен содержать как минимум две точки, а кольцо `Polygon` (и каждое кольцо в `MultiPolygon`) должно быть замкнутым и содержать как минимум четыре точки (см. [Проверка геометрии](#geometry-validation)). Некорректные документы отклоняются, а не загружаются молча.

Порядок ключей может быть произвольным: `type` верхнего уровня может находиться до или после массива `features`, а внутри объекта геометрии `coordinates` может располагаться до или после `type`.

Вывод схемы возвращает приведённую выше фиксированную схему, поэтому `DESCRIBE` и `SELECT ... FROM format(...)` работают без определения таблицы.

Рассмотрим следующий GeoJSON‑файл `london.geojson`, содержащий различные геометрические типы:

```json theme={null}
{
    "type": "FeatureCollection",
    "features": [
        {
            "type": "Feature",
            "id": "1",
            "geometry": {"type": "Point", "coordinates": [-0.0761, 51.5081]},
            "properties": {"name": "Tower of London", "feature_type": "landmark", "year_built": 1078}
        },
        {
            "type": "Feature",
            "id": "2",
            "geometry": {
                "type": "LineString",
                "coordinates": [[-0.2500, 51.4700], [-0.1800, 51.4900], [-0.1200, 51.5060], [-0.0700, 51.5050], [0.0000, 51.5100]]
            },
            "properties": {"name": "River Thames", "feature_type": "river", "length_km": 346}
        },
        {
            "type": "Feature",
            "id": "3",
            "geometry": {
                "type": "Polygon",
                "coordinates": [[[-0.1880, 51.5074], [-0.1533, 51.5074], [-0.1533, 51.5153], [-0.1880, 51.5153], [-0.1880, 51.5074]]]
            },
            "properties": {"name": "Hyde Park", "feature_type": "park", "area_km2": 1.42}
        }
    ]
}
```

Можно выполнить запрос к файлу и посмотреть геометрические типы:

```sql title="Query" theme={null}
SELECT id, properties.name AS name, variantType(geometry) AS geo_type
FROM file('london.geojson', GeoJSON);
```

```response title="Response" theme={null}
┌─id─┬─name────────────┬─geo_type───┐
│ 1  │ Tower of London │ Point      │
│ 2  │ River Thames    │ LineString │
│ 3  │ Hyde Park       │ Polygon    │
└────┴─────────────────┴────────────┘
```

Расширение файла `.geojson` определяется автоматически, поэтому аргумент `format` можно не указывать:

```sql title="Query" theme={null}
SELECT id, properties.name AS name, variantType(geometry) AS geo_type
FROM file('london.geojson');
```

Мы можем использовать `variantType`, чтобы определить базовый тип каждого объекта Geometry:

```sql title="Query" theme={null}
SELECT properties.name AS name, geometry, variantType(geometry)
FROM file('london.geojson', GeoJSON);
```

```response title="Response" theme={null}
Row 1:
──────
name:                  Tower of London
geometry:              (-0.0761,51.5081)
variantType(geometry): Point

Row 2:
──────
name:                  River Thames
geometry:              [(-0.25,51.47),(-0.18,51.49),(-0.12,51.506),(-0.07,51.505),(0,51.51)]
variantType(geometry): LineString

Row 3:
──────
name:                  Hyde Park
geometry:              [[(-0.188,51.5074),(-0.1533,51.5074),(-0.1533,51.5153),(-0.188,51.5153),(-0.188,51.5074)]]
variantType(geometry): Polygon
```

И мы можем извлечь исходные данные следующим образом:

```sql title="Query" theme={null}
SELECT properties.name AS name, variantType(geometry), geometry.Point, geometry.LineString, geometry.Polygon
FROM file('london.geojson', GeoJSON);
```

```response title="Response" theme={null}
Row 1:
──────
name:                  Tower of London
variantType(geometry): Point
geometry.Point:        (-0.0761,51.5081)
geometry.LineString:   []
geometry.Polygon:      []

Row 2:
──────
name:                  River Thames
variantType(geometry): LineString
geometry.Point:        (0,0)
geometry.LineString:   [(-0.25,51.47),(-0.18,51.49),(-0.12,51.506),(-0.07,51.505),(0,51.51)]
geometry.Polygon:      []

Row 3:
──────
name:                  Hyde Park
variantType(geometry): Polygon
geometry.Point:        (0,0)
geometry.LineString:   []
geometry.Polygon:      [[(-0.188,51.5074),(-0.1533,51.5074),(-0.1533,51.5153),(-0.188,51.5153),(-0.188,51.5074)]]
```

При обращении к подстолбцу `Geometry` возвращается значение, если в строке хранится этот тип; в противном случае возвращается значение по умолчанию для этого типа — `(0,0)` для `Point` и `[]` для типов на основе массивов, — поэтому используйте `variantType(geometry)`, чтобы определить, какой именно тип установлен.

Мы также можем загружать данные GeoJSON в таблицу:

```sql title="Query" theme={null}
CREATE TABLE london
(
    id           String,
    geometry     Geometry,
    properties   Nullable(JSON),
    name         String MATERIALIZED properties.name,
    feature_type String MATERIALIZED properties.feature_type
)
ENGINE = MergeTree
ORDER BY id;

INSERT INTO london
SELECT id, geometry, properties
FROM file('london.geojson', GeoJSON);
```

Затем выполните запрос с фильтрацией по типу объекта:

```sql title="Query" theme={null}
SELECT name, feature_type, variantType(geometry) AS geo_type
FROM london
ORDER BY id;
```

```response title="Response" theme={null}
┌─name────────────┬─feature_type─┬─geo_type───┐
│ Tower of London │ landmark     │ Point      │
│ River Thames    │ river        │ LineString │
│ Hyde Park       │ park         │ Polygon    │
└─────────────────┴──────────────┴────────────┘
```

Мы также можем определить схему данных GeoJSON без определения таблицы:

```sql title="Query" theme={null}
DESCRIBE format(GeoJSON, '{"type":"FeatureCollection","features":[]}');
```

```response title="Response" theme={null}
┌─name───────┬─type─────────────┐
│ id         │ Nullable(String) │
│ geometry   │ Geometry         │
│ properties │ Nullable(JSON)   │
└────────────┴──────────────────┘
```

<div id="unsupported-geometry">
  ### Обработка неподдерживаемых геометрических типов
</div>

Некоторые допустимые геометрические типы GeoJSON — такие как `GeometryCollection` и `MultiPoint` — не могут быть представлены типом `Geometry` в ClickHouse. Управлять тем, что происходит, когда такую геометрию нужно сохранить в столбце `geometry`, можно с помощью настройки `input_format_geojson_unsupported_geometry_handling`. Возможные значения:

* `'throw'` — сгенерировать исключение (по умолчанию)
* `'null'` — вставить значение `NULL` в столбец `geometry` и продолжить синтаксический разбор

Эта обработка применяется только при чтении столбца `geometry`. Если `geometry` не входит в число запрошенных выходных столбцов (например, `SELECT id FROM ...`), неподдерживаемая геометрия всё равно проверяется на корректность формата, но эта обработка не срабатывает: исключение не генерируется и `NULL` не вставляется, поскольку значение геометрии не материализуется.

<div id="reading-limitations">
  ### Ограничения
</div>

При чтении сохраняется только то, что укладывается в фиксированную схему, поэтому часть информации GeoJSON теряется:

* Формируются только `id`, `geometry` и `properties`; остальная структура документа не выводится в виде столбцов.
* Третья координата позиции (высота) и все последующие отбрасываются — позиции преобразуются в `[longitude, latitude]`.
* `bbox` и посторонние элементы (например, `name` или `crs` верхнего уровня либо дополнительные элементы внутри `Feature`) игнорируются.
* Числовой `id` сохраняется как текст, поэтому различие между строкой и числом теряется; отсутствующий или `null` `id` становится `NULL`.
* `GeometryCollection` и `MultiPoint` не могут быть представлены — см. [Обработка неподдерживаемых геометрических типов](#unsupported-geometry).

<div id="writing-data">
  ## Запись данных
</div>

При записи результирующего набора создается один GeoJSON [`FeatureCollection`](https://datatracker.ietf.org/doc/html/rfc7946#section-3.3): по одному `Feature` на каждую строку.

Столбцы результата сопоставляются с каждым `Feature` следующим образом:

| Элемент Feature | Формируется из                            | Примечания                                                                                                                                                                                                                                                                                                                                                             |
| --------------- | ----------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `type`          | —                                         | Всегда `"Feature"`.                                                                                                                                                                                                                                                                                                                                                    |
| `geometry`      | единственный столбец геометрического типа | Требуется ровно один столбец геометрического типа, иначе запрос отклоняется. Геометрия `NULL` записывается как `null`.                                                                                                                                                                                                                                                 |
| `id`            | столбец с именем `id`                     | Опускается, если значение равно `NULL`. Столбец `String` записывается как JSON-строка, а числовой столбец — как число JSON.                                                                                                                                                                                                                                            |
| `properties`    | все остальные столбцы                     | Если есть один столбец с именем `properties` и объектоподобным типом (`JSON`, `Map` или именованный `Tuple`), он записывается напрямую как объект `properties`, а не вкладывается под ключ `properties`. В противном случае каждый оставшийся столбец становится отдельным свойством с ключом, равным его имени (если таких столбцов нет, записывается пустой объект). |

Столбец геометрического типа может иметь тип `Geometry` или конкретный геотип; каждому из них соответствует свой тип геометрии GeoJSON:

| Тип ClickHouse    | GeoJSON `"type"`                    |
| ----------------- | ----------------------------------- |
| `Point`           | `Point`                             |
| `LineString`      | `LineString`                        |
| `MultiLineString` | `MultiLineString`                   |
| `Polygon`         | `Polygon`                           |
| `MultiPolygon`    | `MultiPolygon`                      |
| `Ring`            | `Polygon` (одно кольцо)             |
| `Geometry`        | тип активного варианта (или `null`) |

`Ring` не является типом геометрии GeoJSON — [линейное кольцо](https://datatracker.ietf.org/doc/html/rfc7946#section-3.1.6) является компонентом `Polygon` — поэтому значение `Ring` записывается как `Polygon` с одним кольцом.

<div id="writing-examples">
  ### Примеры
</div>

Продолжая работу с таблицей `london`, [созданной выше](#reading-data), экспорт обычных столбцов атрибутов превращает каждый столбец, кроме `id` и `geometry`, в свойство:

```sql title="Query" theme={null}
SELECT id, geometry, name, feature_type
FROM london
ORDER BY id
FORMAT GeoJSON;
```

```response title="Response" theme={null}
{"type":"FeatureCollection","features":[{"type":"Feature","id":"1","geometry":{"type":"Point","coordinates":[-0.0761,51.5081]},"properties":{"name":"Tower of London","feature_type":"landmark"}},{"type":"Feature","id":"2","geometry":{"type":"LineString","coordinates":[[-0.25,51.47],[-0.18,51.49],[-0.12,51.506],[-0.07,51.505],[0,51.51]]},"properties":{"name":"River Thames","feature_type":"river"}},{"type":"Feature","id":"3","geometry":{"type":"Polygon","coordinates":[[[-0.188,51.5074],[-0.1533,51.5074],[-0.1533,51.5153],[-0.188,51.5153],[-0.188,51.5074]]]},"properties":{"name":"Hyde Park","feature_type":"park"}}]}
```

Поскольку единственный столбец типа `object` с именем `properties` записывается напрямую, при чтении GeoJSON‑файла и последующей записи обратно документ воспроизводится в исходном виде (для этого файла автоматически определяются столбцы `id`, `geometry` и `properties`):

```sql title="Query" theme={null}
SELECT * FROM file('london.geojson', GeoJSON) FORMAT GeoJSON;
```

```response title="Response" theme={null}
{"type":"FeatureCollection","features":[{"type":"Feature","id":"1","geometry":{"type":"Point","coordinates":[-0.0761,51.5081]},"properties":{"feature_type":"landmark","name":"Tower of London","year_built":1078}},{"type":"Feature","id":"2","geometry":{"type":"LineString","coordinates":[[-0.25,51.47],[-0.18,51.49],[-0.12,51.506],[-0.07,51.505],[0,51.51]]},"properties":{"feature_type":"river","length_km":346,"name":"River Thames"}},{"type":"Feature","id":"3","geometry":{"type":"Polygon","coordinates":[[[-0.188,51.5074],[-0.1533,51.5074],[-0.1533,51.5153],[-0.188,51.5153],[-0.188,51.5074]]]},"properties":{"area_km2":1.42,"feature_type":"park","name":"Hyde Park"}}]}
```

Числовой столбец `id` записывается как число в JSON (`Nullable` `id` со значением `NULL` опускается полностью):

```sql title="Query" theme={null}
SELECT 42 AS id, (-0.1276, 51.5072)::Point AS geometry FORMAT GeoJSON;
```

```response title="Response" theme={null}
{"type":"FeatureCollection","features":[{"type":"Feature","id":42,"geometry":{"type":"Point","coordinates":[-0.1276,51.5072]},"properties":{}}]}
```

`Ring` записывается как `Polygon` с одним кольцом:

```sql title="Query" theme={null}
SELECT [(0., 0.), (10., 0.), (10., 10.), (0., 0.)]::Ring AS geometry FORMAT GeoJSON;
```

```response title="Response" theme={null}
{"type":"FeatureCollection","features":[{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[0,0],[10,0],[10,10],[0,0]]]},"properties":{}}]}
```

<div id="writing-to-a-file">
  ### Запись в файл
</div>

Используйте `INTO OUTFILE`, чтобы записать GeoJSON‑файл на стороне клиента:

```sql title="Query" theme={null}
SELECT id, geometry, properties
FROM london
ORDER BY id
INTO OUTFILE 'london_export.geojson'
FORMAT GeoJSON;
```

Сервер может сам записать файл с помощью табличной функции `file` (расширение `.geojson` автоматически определяет формат):

```sql title="Query" theme={null}
INSERT INTO FUNCTION file('london_export.geojson', GeoJSON)
SELECT id, geometry, properties FROM london;
```

<div id="reading-limitations">
  ### Ограничения
</div>

<Note>
  Гео-типы ClickHouse не содержат сведений о системе координат, поэтому в выходных данных предполагается, что координаты уже заданы в WGS84 как долгота/широта в порядке `[longitude, latitude]`, как того требует [RFC 7946](https://datatracker.ietf.org/doc/html/rfc7946#section-4). Ни перепроецирование, ни перестановка осей не выполняются, поэтому спроецированные координаты — или данные, сохранённые как `(latitude, longitude)` — дают структурно корректный, но не соответствующий стандарту GeoJSON.
</Note>

Выходные данные отражают только то, что хранится в ClickHouse:

* Информация, отброшенная при чтении, — высота точки, `bbox`, сторонние поля и различие между строковым и числовым `id` — не может быть восстановлена; см. [Ограничения чтения](#reading-limitations).
* Координаты записываются из значений `Float64` с использованием их кратчайшего представления, допускающего обратимое преобразование.
* Объект `properties`, взятый напрямую из столбца `JSON`, выводится в каноническом порядке ключей типа `JSON`, который может отличаться от исходного.

Геометрии записываются в точности в том виде, в каком они хранятся: порядок координат и направление обхода сохраняются. По умолчанию при записи проверяется корректность GeoJSON-формы (см. [Проверка геометрии](#geometry-validation)): геометрия, не являющаяся допустимой GeoJSON-формой, например `LineString` с одной точкой или незамкнутое кольцо `Polygon`, отклоняется, чтобы записанный документ можно было затем прочитать обратно. Если вместо этого задать `format_geojson_validate_geometry = 0`, такие геометрии будут выводиться как есть, образуя структурно корректный, но не соответствующий стандарту GeoJSON. Инвариант правила правой руки (направление обхода) не проверяется ни в одном из режимов, а различие между `null` и пустым объектом `properties` сохраняется.

<div id="geometry-validation">
  ## Проверка геометрии
</div>

Параметр `format_geojson_validate_geometry` определяет, проверяет ли формат соблюдение правил формы геометрии из [RFC 7946](https://datatracker.ietf.org/doc/html/rfc7946#section-3.1) в обоих направлениях. По умолчанию он включен.

Если параметр включен, геометрия, нарушающая правила формы GeoJSON, отклоняется: `LineString` (или линия в `MultiLineString`) с менее чем двумя точками; кольцо `Polygon` или `MultiPolygon` с менее чем четырьмя точками либо с несовпадающими первой и последней точками (незамкнутое кольцо); а также пустой `MultiLineString`, `Polygon` или `MultiPolygon`. Те же правила действуют как при чтении такого документа, так и при записи такого значения ClickHouse, поэтому записанный документ всегда можно прочитать обратно.

Если параметр отключен, эти правила формы не проверяются ни в одном направлении: вырожденные геометрии читаются и записываются как есть. Это позволяет значениям геометрии ClickHouse, не являющимся корректными геометриями GeoJSON, проходить через формат без изменений, ценой создания документов, не являющихся корректным GeoJSON.

Проверка носит исключительно структурный характер: проверяются только количество точек и замкнутость колец. Геометрическая корректность формы не анализируется, поэтому структурно корректная, но геометрически вырожденная геометрия принимается в обоих направлениях — например, полигон нулевой площади, самопересекающееся кольцо или полигон, чьи дыры (внутренние кольца) лежат вне его внешнего кольца. Ориентация колец полигона по правилу правой руки (направление обхода) также никогда не проверяется.

Одна проверка не зависит от этого параметра: нечисловые координаты (`NaN`, `Inf`) всегда отклоняются, поскольку их нельзя представить в виде чисел JSON.
