Skip to content
ClickHouse Docs
ClickHouse DocsClickHouse Docs

GeoJSON

Entrada Salida Alias

Descripción

Los datos GeoJSON se intercambian en un único documento FeatureCollection, que ClickHouse asigna a tres columnas — id, geometry y properties —, un conjunto por cada Feature. Al leer un documento, se produce una fila por Feature; al escribir, se produce una Feature por fila.

Lectura de datos

Leer una FeatureCollection produce una fila por característica con el siguiente esquema fijo:

Columna Tipo Descripción
id Nullable(String) El miembro id de la entidad (una cadena JSON o un número), almacenado como texto; NULL si id no está presente o es null, mientras que un id explícito de cadena vacía se conserva como ''.
geometry Geometry La geometría de la entidad, almacenada como un tipo variante Geometry.
properties Nullable(JSON) El objeto properties de la entidad, almacenado como una columna JSON semiestructurada. Un valor explícito de "properties": null`` se conserva como NULL`.

Cada geometría se almacena en el tipo Geometry de ClickHouse (un Variant). Los tipos de geometría GeoJSON admitidos son Point, MultiPoint, LineString, MultiLineString, Polygon y MultiPolygon. El tipo de geometría GeoJSON restante, GeometryCollection, no puede representarse con el tipo Geometry; leer uno en la columna geometry genera una excepción de forma predeterminada, lo que puede cambiarse para insertar NULL en su lugar — consulte Manejo de tipos de geometría no admitidos más abajo. De forma predeterminada, la columna geometry es NULL únicamente cuando la geometría de una entidad es un null JSON explícito; con input_format_geojson_unsupported_geometry_handling = 'null' también es NULL para un tipo de geometría no admitido.

Se valida la estructura del documento: el type de nivel superior debe ser FeatureCollection y cada elemento de features debe tener type Feature. De forma predeterminada, las coordenadas deben cumplir las invariantes de forma de GeoJSON: un LineString (y cada línea de un MultiLineString) debe tener al menos dos puntos, y un anillo de Polygon (y cada anillo de un MultiPolygon) debe estar cerrado y tener al menos cuatro puntos (consulta Validación de geometrías). Los documentos malformados se rechazan en lugar de cargarse silenciosamente.

El orden de las claves es flexible: el type de nivel superior puede aparecer antes o después del array features, y dentro de un objeto de geometría coordinates puede aparecer antes o después de type.

La inferencia de esquemas devuelve el esquema fijo anterior, por lo que DESCRIBE y SELECT ... FROM format(...) funcionan sin una definición de tabla.

Dado el siguiente archivo GeoJSON london.geojson, que contiene una combinación de tipos de geometría:

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

Podemos consultar el archivo y examinar los tipos de geometría:

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    │
└────┴─────────────────┴────────────┘

La extensión de archivo .geojson se detecta automáticamente, por lo que el argumento de formato puede omitirse:

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

Podemos usar variantType para saber el tipo subyacente de cada objeto de tipo 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

Y podemos extraer los datos subyacentes así:

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)]]

Al acceder a una subcolumna Geometry, se devuelve el valor cuando la fila contiene ese tipo y, en caso contrario, el valor predeterminado del tipo — (0,0) para Point y [] para los tipos basados en arrays —, así que usa variantType(geometry) para saber cuál está definido.

También podemos ingestar datos GeoJSON en una tabla:

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

Luego, consulta por tipo de entidad:

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    │
└─────────────────┴──────────────┴────────────┘

También podemos inferir el esquema de los datos en GeoJSON sin una definición de tabla:

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

Manejo de tipos de geometría no admitidos

Algunos tipos de geometría GeoJSON válidos — como GeometryCollection — no pueden representarse con el tipo Geometry de ClickHouse. Puede controlar qué ocurre cuando debe almacenarse una geometría de este tipo en la columna geometry mediante la configuración input_format_geojson_unsupported_geometry_handling. Los valores posibles son:

  • 'throw' — lanzar una excepción (predeterminado)
  • 'null' — insertar un valor NULL en la columna geometry y continuar el análisis

Este comportamiento se aplica solo cuando se lee la columna geometry. Cuando geometry no es una columna de salida solicitada (por ejemplo, SELECT id FROM ...), una geometría no admitida sigue validándose para comprobar que esté bien formada, pero no activa este comportamiento: ni lanza una excepción ni inserta NULL, porque no se materializa ningún valor de geometría.

Limitaciones

La lectura refleja solo lo que encaja en el esquema fijo, por lo que no se conserva parte de la información de GeoJSON:

  • Solo se generan id, geometry y properties; el resto de la estructura del documento no se expone como columnas.
  • La tercera coordenada (elevación) de una posición, así como cualquier coordenada posterior, se descartan; las posiciones pasan a ser [longitud, latitud].
  • bbox y los miembros ajenos (como un name o crs de nivel superior, o miembros adicionales dentro de una Feature) se ignoran.
  • Un id numérico se almacena como texto, por lo que se pierde la distinción entre cadena y número; un id ausente o null pasa a ser NULL.
  • GeometryCollection no puede representarse; consulta manejo de tipos de geometría no admitidos.

Escritura de datos

Escribir un conjunto de resultados genera una única FeatureCollection de GeoJSON, con una Feature por fila.

Las columnas del resultado se asignan a cada Feature de la siguiente manera:

Miembro de Feature Se construye a partir de Notas
type Siempre "Feature".
geometry la única columna de tipo geometría Se requiere exactamente una columna de tipo geometría; de lo contrario, la consulta se rechaza. Una geometría NULL se escribe como null.
id una columna llamada id Se omite cuando el valor es NULL. Una columna String se escribe como una cadena JSON y una columna numérica, como un número JSON.
properties todas las columnas restantes Una única columna llamada properties cuyo tipo es similar a un objeto (JSON, Map o un Tuple con nombre) se escribe directamente como el objeto properties, en lugar de anidarse bajo una clave properties. En caso contrario, cada columna restante se convierte en una propiedad identificada por su nombre (un objeto vacío cuando no hay ninguna).

La columna de tipo geometría puede ser la variante Geometry o un tipo geo específico; cada uno se asigna a un tipo de geometría GeoJSON:

Tipo de ClickHouse "type" de GeoJSON
Point Point
MultiPoint MultiPoint
LineString LineString
MultiLineString MultiLineString
Polygon Polygon
MultiPolygon MultiPolygon
Ring Polygon (un solo anillo)
Geometry el tipo de la variante activa (o null)

Ring no es un tipo de geometría de GeoJSON: un anillo lineal es un componente de un Polygon, por lo que un valor Ring se escribe como un Polygon de un solo anillo.

Ejemplos

Siguiendo con la tabla london creada anteriormente, al exportar columnas de atributo simples, cada columna distinta de id y geometry se convierte en una propiedad:

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

Como una única columna de tipo object llamada properties se escribe directamente, al leer un archivo GeoJSON y volver a escribirlo sin más se reproduce el documento (las columnas id, geometry y properties son las que se infieren para el archivo):

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

Una columna id numérica se escribe como un número JSON (un id Nullable que es NULL se omite por completo):

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

Un Ring se representa como un Polygon de un solo anillo:

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

Escribir en un archivo

Use INTO OUTFILE para escribir un archivo GeoJSON desde el client:

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

El servidor puede escribir el archivo directamente con la función de tabla file (la extensión .geojson selecciona el formato automáticamente):

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

Limitaciones

La salida refleja únicamente lo que almacena ClickHouse:

  • La información descartada durante la lectura — la elevación de una posición, bbox, miembros ajenos y la distinción entre cadena y número de un id — no puede reproducirse; consulte Limitaciones de lectura.
  • Las coordenadas se escriben a partir de valores Float64 usando su representación más corta que permite ida y vuelta.
  • Un objeto properties tomado directamente de una columna JSON se emite en el orden canónico de claves del tipo JSON, que puede diferir de la entrada.

Las geometrías se escriben exactamente como se almacenan: se conservan el orden de las coordenadas y la orientación. De forma predeterminada, la validez de la forma GeoJSON se comprueba al escribir (consulte validación de geometrías): una geometría que no sea una forma GeoJSON válida, como un LineString con un solo punto o un anillo de Polygon no cerrado, se rechaza para garantizar que el documento escrito pueda volver a leerse. Establezca format_geojson_validate_geometry = 0 para emitir estas geometrías tal cual, produciendo en su lugar GeoJSON estructuralmente válido, pero no conforme. La regla de la mano derecha (orientación) no se aplica en ningún caso, y se conserva la distinción entre null y un objeto properties vacío.

Validación de geometrías

La configuración format_geojson_validate_geometry controla si el formato hace cumplir las reglas de forma geométrica de RFC 7946 en ambos sentidos. Está habilitada de forma predeterminada.

Cuando está habilitada, se rechaza cualquier geometría que incumpla las reglas de forma de GeoJSON: un LineString (o una línea de un MultiLineString) con menos de dos puntos; un anillo de Polygon o MultiPolygon con menos de cuatro puntos, o cuyo primer y último punto difieren (un anillo no cerrado); o un MultiLineString, Polygon o MultiPolygon vacío. Las mismas reglas se aplican tanto al leer un documento de este tipo como al escribir un valor de ClickHouse de este tipo, por lo que un documento escrito siempre puede volver a leerse.

Cuando está deshabilitada, estas reglas de forma no se aplican en ninguno de los dos sentidos: las geometrías degeneradas se leen tal cual y se escriben tal cual. Esto permite que los valores geométricos de ClickHouse que no son geometrías GeoJSON válidas puedan pasar de ida y vuelta por el formato, a costa de producir documentos que no son GeoJSON válidos.

La validación es solo estructural: comprueba el recuento de puntos y el cierre de los anillos. No examina la corrección geométrica de una forma, por lo que se acepta en ambos sentidos una geometría estructuralmente válida pero geométricamente degenerada; por ejemplo, un polígono de área cero, un anillo que se autointerseca o un polígono cuyos agujeros (anillos interiores) están fuera de su anillo exterior. Del mismo modo, nunca se aplica la orientación de los anillos de los polígonos según la regla de la mano derecha (sentido de recorrido).

Una comprobación es independiente de la configuración: las coordenadas no finitas (NaN, Inf) siempre se rechazan, porque no pueden representarse como números JSON.

Navigation