> ## 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.

> Format d’entrée et de sortie pour les documents FeatureCollection GeoJSON : en entrée, une ligne par feature, avec les colonnes id, geometry et properties ; en sortie, une feature par ligne.

# GeoJSON

| Entrée | Sortie | Alias |
| ------ | ------ | ----- |
| ✔      | ✔      |       |

<div id="description">
  ## Description
</div>

Les données [GeoJSON](https://geojson.org/) sont échangées sous la forme d’un unique document [`FeatureCollection`](https://datatracker.ietf.org/doc/html/rfc7946#section-3.3), que ClickHouse associe à trois colonnes — `id`, `geometry` et `properties` — soit un ensemble par `Feature`. La [lecture](#reading-data) d’un document produit une ligne par feature ; l’[écriture](#writing-data) produit une feature par ligne.

<div id="reading-data">
  ## Lecture des données
</div>

La lecture d’une `FeatureCollection` produit une ligne par feature avec le schéma fixe suivant :

| Colonne      | Type               | Description                                                                                                                                                                                                 |
| ------------ | ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`         | `Nullable(String)` | Le membre `id` de la feature (une chaîne JSON ou un nombre), stocké sous forme de texte ; `NULL` si `id` est absent ou `null`, tandis qu’un identifiant explicitement vide est conservé sous la forme `''`. |
| `geometry`   | `Geometry`         | La géométrie de la feature, stockée dans un type `Geometry` variant.                                                                                                                                        |
| `properties` | `Nullable(JSON)`   | L’objet `properties` de la feature, stocké dans une colonne `JSON` semi-structurée. Une valeur explicite `"properties": null` est conservée sous forme de `NULL`.                                           |

Chaque géométrie est stockée dans le type `Geometry` de ClickHouse (un `Variant`). Les types de géométrie GeoJSON pris en charge sont `Point`, `LineString`, `MultiLineString`, `Polygon` et `MultiPolygon`. Les deux autres types de géométrie GeoJSON, `GeometryCollection` et `MultiPoint`, ne peuvent pas être représentés par le type `Geometry` ; la lecture de l’un d’eux dans la colonne `geometry` lève par défaut une exception, mais ce comportement peut être modifié pour insérer `NULL` à la place — voir [Gestion des types de géométrie non pris en charge](#unsupported-geometry) ci-dessous. Par défaut, la colonne `geometry` vaut `NULL` uniquement lorsque la géométrie d’une feature est un `null` JSON explicite ; avec `input_format_geojson_unsupported_geometry_handling = 'null'`, elle vaut également `NULL` pour un type de géométrie non pris en charge.

La structure du document est validée : le `type` de premier niveau doit être `FeatureCollection` et chaque élément de `features` doit avoir pour `type` `Feature`. Par défaut, les coordonnées doivent respecter les invariants de forme du GeoJSON — une `LineString` (et chaque ligne d’une `MultiLineString`) doit comporter au moins deux points, et un anneau de `Polygon` (ainsi que chaque anneau d’un `MultiPolygon`) doit être fermé et comporter au moins quatre points (voir [Validation de la géométrie](#geometry-validation)). Les documents malformés sont rejetés plutôt que chargés silencieusement.

L’ordre des clés est souple : le `type` de premier niveau peut apparaître avant ou après le tableau `features`, et dans un objet géométrique, `coordinates` peut apparaître avant ou après `type`.

L’inférence de schéma renvoie le schéma fixe ci-dessus, de sorte que `DESCRIBE` et `SELECT ... FROM format(...)` fonctionnent sans définition de table.

Considérons le fichier GeoJSON `london.geojson` ci-dessous, qui contient un mélange de types de géométrie :

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

Nous pouvons interroger le fichier et examiner les types de géométrie :

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

L’extension de fichier `.geojson` est détectée automatiquement ; l’argument de format peut donc être omis :

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

Nous pouvons utiliser `variantType` pour vérifier le type sous-jacent de chaque objet 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
```

Et nous pouvons extraire les données sous-jacentes de cette manière :

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

L’accès à une sous-colonne `Geometry` renvoie la valeur si la ligne contient ce type, et sinon la valeur par défaut du type — `(0,0)` pour `Point` et `[]` pour les types basés sur des tableaux — ; utilisez donc `variantType(geometry)` pour déterminer lequel est présent.

Nous pouvons également ingérer des données GeoJSON dans une table :

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

Ensuite, effectuez une requête par type d’entité :

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

Nous pouvons également inférer le schéma des données GeoJSON sans définir de table :

```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">
  ### Gestion des types de géométrie non pris en charge
</div>

Certains types de géométrie GeoJSON valides — tels que `GeometryCollection` et `MultiPoint` — ne peuvent pas être représentés par le type `Geometry` de ClickHouse. Vous pouvez contrôler le comportement lorsqu'une telle géométrie doit être stockée dans la colonne `geometry` à l'aide du paramètre `input_format_geojson_unsupported_geometry_handling`. Les valeurs possibles sont :

* `'throw'` — générer une exception (par défaut)
* `'null'` — insérer une valeur `NULL` dans la colonne `geometry` et continuer l'analyse

Ce comportement s'applique uniquement lorsque la colonne `geometry` est lue. Lorsque `geometry` ne fait pas partie des colonnes de sortie demandées (par exemple `SELECT id FROM ...`), une géométrie non prise en charge est tout de même validée pour vérifier qu'elle est bien formée, mais ne déclenche pas ce traitement : elle ne génère pas d'exception et n'insère pas non plus `NULL`, car aucune valeur géométrique n'est matérialisée.

<div id="reading-limitations">
  ### Limites
</div>

La lecture ne restitue que ce qui correspond au schéma fixe ; certaines informations GeoJSON ne sont donc pas conservées :

* Seuls `id`, `geometry` et `properties` sont produits ; les autres éléments de la structure du document ne sont pas exposés sous forme de colonnes.
* La troisième coordonnée (altitude) d’une position, ainsi que toutes les suivantes, sont supprimées — les positions deviennent `[longitude, latitude]`.
* `bbox` et les membres externes (comme un `name` ou un `crs` au niveau supérieur, ou des membres supplémentaires dans une `Feature`) sont ignorés.
* Un `id` numérique est stocké sous forme de texte ; la distinction entre chaîne et nombre est donc perdue ; un `id` absent ou `null` devient `NULL`.
* `GeometryCollection` et `MultiPoint` ne peuvent pas être représentés — voir [Gestion des types de géométrie non pris en charge](#unsupported-geometry).

<div id="writing-data">
  ## Écriture des données
</div>

L'écriture d'un jeu de résultats produit une seule [`FeatureCollection`](https://datatracker.ietf.org/doc/html/rfc7946#section-3.3) GeoJSON, avec une `Feature` par ligne.

Les colonnes du résultat sont associées à chaque `Feature` comme suit :

| Membre de `Feature` | Construit à partir de                | Notes                                                                                                                                                                                                                                                                                                                                 |
| ------------------- | ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `type`              | —                                    | Toujours `"Feature"`.                                                                                                                                                                                                                                                                                                                 |
| `geometry`          | l'unique colonne de type géométrique | Une seule colonne de type géométrique est autorisée, faute de quoi la requête est rejetée. Une géométrie `NULL` est écrite comme `null`.                                                                                                                                                                                              |
| `id`                | une colonne nommée `id`              | Omis lorsque la valeur est `NULL`. Une colonne `String` est écrite comme une chaîne JSON, une colonne numérique comme un nombre JSON.                                                                                                                                                                                                 |
| `properties`        | toutes les colonnes restantes        | Une colonne unique nommée `properties`, dont le type est assimilable à un objet (`JSON`, `Map` ou `Tuple` nommé), est écrite directement comme objet `properties` au lieu d'être imbriquée sous une clé `properties`. Sinon, chaque colonne restante devient une propriété dont la clé est son nom (objet vide s'il n'y en a aucune). |

La colonne de type géométrique peut être la variante `Geometry` ou un type Geo spécifique ; chacune correspond à un type de géométrie GeoJSON :

| Type ClickHouse   | GeoJSON `"type"`                          |
| ----------------- | ----------------------------------------- |
| `Point`           | `Point`                                   |
| `LineString`      | `LineString`                              |
| `MultiLineString` | `MultiLineString`                         |
| `Polygon`         | `Polygon`                                 |
| `MultiPolygon`    | `MultiPolygon`                            |
| `Ring`            | `Polygon` (un seul anneau)                |
| `Geometry`        | le type de la variante active (ou `null`) |

`Ring` n'est pas un type de géométrie GeoJSON — un [anneau linéaire](https://datatracker.ietf.org/doc/html/rfc7946#section-3.1.6) est un composant d'un `Polygon` — donc une valeur `Ring` est écrite comme un `Polygon` à anneau unique.

<div id="writing-examples">
  ### Exemples
</div>

En reprenant la table `london` [créée ci-dessus](#reading-data), l’export de colonnes d’attribut simples transforme chaque colonne autre que `id` et `geometry` en propriété :

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

Comme une seule colonne de type objet nommée `properties` est écrite telle quelle, lire un fichier GeoJSON puis le réécrire tel quel reproduit le document (les colonnes `id`, `geometry` et `properties` sont celles déduites pour le fichier) :

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

Une colonne `id` numérique est représentée sous forme de nombre JSON (un `id` `Nullable` qui est `NULL` est entièrement omis) :

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

Un `Ring` s'écrit sous la forme d'un `Polygon` à anneau unique :

```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">
  ### Écriture dans un fichier
</div>

Utilisez `INTO OUTFILE` pour écrire un fichier GeoJSON côté client :

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

Le serveur peut lui-même écrire le fichier avec la fonction de table `file` (l’extension `.geojson` sélectionne automatiquement le format) :

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

<div id="writing-limitations">
  ### Limitations
</div>

<Note>
  Les types géo de ClickHouse n’intègrent aucun système de référence de coordonnées. La sortie suppose donc que les coordonnées sont déjà en WGS84 longitude/latitude, dans l’ordre `[longitude, latitude]`, comme l’exige la [RFC 7946](https://datatracker.ietf.org/doc/html/rfc7946#section-4). Aucune reprojection ni permutation d’axes n’est effectuée. Par conséquent, des coordonnées projetées — ou des données stockées sous la forme `(latitude, longitude)` — produisent un GeoJSON structurellement valide, mais non conforme.
</Note>

La sortie reflète uniquement ce que ClickHouse stocke :

* Les informations perdues à la lecture — l’altitude d’une position, `bbox`, les membres supplémentaires et la distinction entre chaîne et nombre pour un `id` — ne peuvent pas être restituées ; voir [Reading limitations](#reading-limitations).
* Les coordonnées sont écrites à partir de valeurs `Float64` en utilisant leur représentation réversible la plus courte.
* Un objet `properties` pris directement depuis une colonne `JSON` est émis dans l’ordre canonique des clés du type `JSON`, qui peut différer de l’entrée.

Les géométries sont écrites exactement telles qu’elles sont stockées — l’ordre des coordonnées et le sens d’enroulement sont conservés. Par défaut, la validité de la géométrie GeoJSON est vérifiée à l’écriture (voir [Geometry validation](#geometry-validation)) : une géométrie qui n’est pas une forme GeoJSON valide, comme une `LineString` avec un seul point ou un anneau de `Polygon` non fermé, est rejetée afin que le document écrit puisse être relu. Définissez `format_geojson_validate_geometry = 0` pour émettre ces géométries telles quelles, ce qui produit un GeoJSON structurellement valide, mais non conforme. L’invariant de la règle de la main droite (sens d’enroulement) n’est appliqué dans aucun des deux cas, et la distinction entre `null` et un objet `properties` vide est conservée.

<div id="geometry-validation">
  ## Validation de la géométrie
</div>

Le paramètre `format_geojson_validate_geometry` détermine si le format applique les règles de forme géométrique de la [RFC 7946](https://datatracker.ietf.org/doc/html/rfc7946#section-3.1), dans les deux sens. Il est activé par défaut.

Lorsqu’elle est activée, une géométrie qui ne respecte pas les règles de forme de GeoJSON est rejetée : une `LineString` (ou une ligne d’une `MultiLineString`) comportant moins de deux points ; un anneau de `Polygon` ou de `MultiPolygon` comportant moins de quatre points, ou dont le premier et le dernier point diffèrent (anneau non fermé) ; ou une `MultiLineString`, un `Polygon` ou un `MultiPolygon` vide. Les mêmes règles s’appliquent aussi bien à la lecture d’un tel document qu’à l’écriture d’une telle valeur ClickHouse, de sorte qu’un document écrit peut toujours être relu.

Lorsqu’elle est désactivée, ces règles de forme ne sont appliquées dans aucun sens : les géométries dégénérées sont lues et écrites telles quelles. Cela permet à des valeurs géométriques ClickHouse qui ne sont pas des géométries GeoJSON valides d’être restituées à l’identique après un aller-retour via ce format, au prix de produire des documents qui ne sont pas des GeoJSON valides.

La validation est uniquement structurelle : elle vérifie le nombre de points et la fermeture des anneaux. Elle n’examine pas la validité géométrique d’une forme ; ainsi, une géométrie structurellement valide mais géométriquement dégénérée est acceptée dans les deux sens — par exemple un polygone d’aire nulle, un anneau auto-intersectant, ou un polygone dont les trous (anneaux intérieurs) se trouvent en dehors de son anneau extérieur. De même, l’orientation des anneaux de polygone selon la règle de la main droite (ordre d’enroulement) n’est jamais imposée.

Une vérification est indépendante du paramètre : les coordonnées non finies (`NaN`, `Inf`) sont toujours rejetées, car elles ne peuvent pas être représentées sous forme de nombres JSON.
