Skip to content
ClickHouse Docs
ClickHouse DocsClickHouse Docs

GeoJSON

Ввод Вывод Псевдоним

Описание

Данные GeoJSON передаются в виде единого документа FeatureCollection, который ClickHouse сопоставляет с тремя столбцами — id, geometry и properties — по одному набору для каждого Feature. Чтение документа даёт по одной строке на каждую возможность, а запись — по одной возможности на строку.

Чтение данных

Чтение FeatureCollection создаёт по одной строке на объект (feature) со следующей фиксированной схемой:

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

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

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

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

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

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

{
    "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}
        }
    ]
}

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

Querysql
SELECT id, properties.name AS name, variantType(geometry) AS geo_type
FROM file('london.geojson', GeoJSON);
Responseresponse
┌─id─┬─name────────────┬─geo_type───┐
│ 1  │ Tower of London │ Point      │
│ 2  │ River Thames    │ LineString │
│ 3  │ Hyde Park       │ Polygon    │
└────┴─────────────────┴────────────┘

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

Querysql
SELECT id, properties.name AS name, variantType(geometry) AS geo_type
FROM file('london.geojson');

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

Querysql
SELECT properties.name AS name, geometry, variantType(geometry)
FROM file('london.geojson', GeoJSON);
Responseresponse
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

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

Querysql
SELECT properties.name AS name, variantType(geometry), geometry.Point, geometry.LineString, geometry.Polygon
FROM file('london.geojson', GeoJSON);
Responseresponse
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 в таблицу:

Querysql
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);

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

Querysql
SELECT name, feature_type, variantType(geometry) AS geo_type
FROM london
ORDER BY id;
Responseresponse
┌─name────────────┬─feature_type─┬─geo_type───┐
│ Tower of London │ landmark     │ Point      │
│ River Thames    │ river        │ LineString │
│ Hyde Park       │ park         │ Polygon    │
└─────────────────┴──────────────┴────────────┘

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

Querysql
DESCRIBE format(GeoJSON, '{"type":"FeatureCollection","features":[]}');
Responseresponse
┌─name───────┬─type─────────────┐
│ id         │ Nullable(String) │
│ geometry   │ Geometry         │
│ properties │ Nullable(JSON)   │
└────────────┴──────────────────┘

Обработка неподдерживаемых геометрических типов

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

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

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

Ограничения

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

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

Запись данных

При записи результирующего набора создается один GeoJSON FeatureCollection: по одному 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
MultiPoint MultiPoint
LineString LineString
MultiLineString MultiLineString
Polygon Polygon
MultiPolygon MultiPolygon
Ring Polygon (одно кольцо)
Geometry тип активного варианта (или null)

Ring не является типом геометрии GeoJSON — линейное кольцо является компонентом Polygon — поэтому значение Ring записывается как Polygon с одним кольцом.

Примеры

Продолжая работу с таблицей london, созданной выше, экспорт обычных столбцов атрибутов превращает каждый столбец, кроме id и geometry, в свойство:

Querysql
SELECT id, geometry, name, feature_type
FROM london
ORDER BY id
FORMAT GeoJSON;
Responseresponse
{"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):

Querysql
SELECT * FROM file('london.geojson', GeoJSON) FORMAT GeoJSON;
Responseresponse
{"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 опускается полностью):

Querysql
SELECT 42 AS id, (-0.1276, 51.5072)::Point AS geometry FORMAT GeoJSON;
Responseresponse
{"type":"FeatureCollection","features":[{"type":"Feature","id":42,"geometry":{"type":"Point","coordinates":[-0.1276,51.5072]},"properties":{}}]}

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

Querysql
SELECT [(0., 0.), (10., 0.), (10., 10.), (0., 0.)]::Ring AS geometry FORMAT GeoJSON;
Responseresponse
{"type":"FeatureCollection","features":[{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[0,0],[10,0],[10,10],[0,0]]]},"properties":{}}]}

Запись в файл

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

Querysql
SELECT id, geometry, properties
FROM london
ORDER BY id
INTO OUTFILE 'london_export.geojson'
FORMAT GeoJSON;

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

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

Ограничения

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

  • Информация, отброшенная при чтении, — высота точки, bbox, сторонние поля и различие между строковым и числовым id — не может быть восстановлена; см. Ограничения чтения.
  • Координаты записываются из значений Float64 с использованием их кратчайшего представления, допускающего обратимое преобразование.
  • Объект properties, взятый напрямую из столбца JSON, выводится в каноническом порядке ключей типа JSON, который может отличаться от исходного.

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

Проверка геометрии

Параметр format_geojson_validate_geometry определяет, проверяет ли формат соблюдение правил формы геометрии из RFC 7946 в обоих направлениях. По умолчанию он включен.

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

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

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

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

Navigation