Réponse courte

Dans PostGIS, geometry et geography sont deux types de colonnes spatiales distincts, porteurs de deux conceptions différentes de ce qu'est la Terre. geometry traite chaque couple de coordonnées comme un point sur un plan plat et mesure avec une arithmétique euclidienne ordinaire ; ses résultats sont donc exprimés dans l'unité du SCR de la colonne (degrés, mètres, pieds). geography traite les coordonnées comme des positions sur le sphéroïde WGS84 et mesure toujours en mètres au moyen de formules géodésiques (orthodromiques). Choisissez geometry avec un SCR projeté quand votre travail tient dans une seule région et que vous voulez de la vitesse et la bibliothèque complète de fonctions ; choisissez geography quand les données sont mondiales en lon/lat et que vous avez besoin de distances correctes sans avoir à choisir de projection.

Comment chaque type stocke et mesure

Une colonne geometry est fondamentalement agnostique vis-à-vis de la planète. PostGIS stocke les coordonnées et une étiquette SRID, mais ST_Distance, ST_Area, ST_Buffer et les autres exécutent une arithmétique cartésienne sur ces nombres bruts. Si la colonne est geometry(Point, 4326), la « distance » entre Paris et Berlin est la distance en ligne droite exprimée en degrés de lon/lat, ce qui n'a aucun sens géométrique en tant que distance au sol. Si la colonne est geometry(Point, 32633) (UTM zone 33N, mètres), le même appel renvoie une distance cohérente en mètres, précise à une fraction de pour cent à l'intérieur de cette zone.

Une colonne geography est conçue pour la Terre courbe. Elle suppose WGS84 (EPSG:4326) par défaut et calcule les distances le long du sphéroïde. ST_Distance(a::geography, b::geography) entre Paris et Berlin renvoie environ 877 000 mètres, la distance orthodromique, quelle que soit leur éloignement ou la zone UTM dans laquelle elles se trouvent. Cette justesse est toute la raison d'être de ce type.

Le compromis est direct : le calcul géodésique est plus coûteux que le calcul planaire, et PostGIS implémente bien moins de fonctions pour geography que pour geometry. Des opérations comme ST_Buffer, ST_Intersection, la superposition et de nombreuses fonctions constructives sont soit non prises en charge sur geography, soit déléguées en silence à une approximation planaire. Ainsi, geography achète la justesse sur de longues distances au prix de la vitesse et de l'étendue des opérations.

Choisir entre les deux

La décision se ramène généralement à l'emprise et aux opérations dont vous avez besoin.

Utilisez geometry dans un SCR projeté quand :

  • Vos données tiennent dans un seul pays, une seule région administrative ou une seule zone UTM. Une zone UTM unique (6 degrés de longitude de large) maintient la distorsion sous environ 0,1 % près du méridien central, ce qui convient à la plupart des travaux d'ingénierie et de géologie.
  • Vous avez besoin de superpositions spatiales, de zones tampons, de découpage, d'accrochage ou de fonctions de topologie.
  • Le débit des requêtes compte ; les prédicats planaires et les index GiST sur des geometry projetées sont rapides.

Utilisez geography quand :

  • Les données sont véritablement mondiales ou continentales et les forcer dans une seule projection en déformerait de grandes parties.
  • Vous n'avez besoin que de distance, longueur, surface, ST_DWithin et de tests d'inclusion, pas de toute la boîte à outils constructive.
  • Vos entités franchissent les limites de zones UTM ou l'antiméridien, là où une seule projection planaire s'effondre.

Un hybride pratique que beaucoup d'équipes adoptent : stocker la colonne canonique en geometry(Point, 4326), construire un index GiST fonctionnel à l'aide d'une conversion vers geography, et laisser le planificateur l'utiliser pour les requêtes de proximité :

CREATE INDEX idx_sites_geog
  ON sites USING gist ((geom::geography));

Cela vous donne une source de vérité en 4326 plus des recherches géodésiques ST_DWithin rapides, sans dupliquer les données.

Exemple résolu : une requête de proximité à 25 km

Supposons que vous ayez une table sites avec geom geometry(Point, 4326) et que vous vouliez tous les sites situés à moins de 25 km d'un forage à lon 2.3522, lat 48.8566.

La mauvaise façon, sur une geometry brute :

-- FAUX : 25000 est interprété comme 25000 DEGRÉS
SELECT id FROM sites
WHERE ST_DWithin(geom, ST_SetSRID(ST_MakePoint(2.3522, 48.8566), 4326), 25000);

Cela renvoie toutes les lignes, car 25 000 degrés représentent plusieurs fois la planète entière. La conversion en geography corrige les unités :

-- CORRECT : 25000 mètres, géodésique
SELECT id FROM sites
WHERE ST_DWithin(
        geom::geography,
        ST_SetSRID(ST_MakePoint(2.3522, 48.8566), 4326)::geography,
        25000);

Ou, si toutes vos données sont régionales, transformez-les une fois vers UTM 31N (EPSG:32631) et restez en mètres planaires :

SELECT id FROM sites
WHERE ST_DWithin(
        ST_Transform(geom, 32631),
        ST_Transform(ST_SetSRID(ST_MakePoint(2.3522, 48.8566), 4326), 32631),
        25000);

Les deux renvoient des résultats corrects en mètres. La version geography est plus simple à raisonner ; la version projetée est plus rapide si vous pré-transformez et indexez la colonne projetée.

ST_SetSRID n'est pas ST_Transform

C'est la confusion la plus dommageable de PostGIS, et elle produit une sortie silencieusement fausse parce que rien ne déclenche d'erreur.

ST_SetSRID(geom, 4326) ne modifie que l'entier SRID estampillé sur la géométrie. Les coordonnées restent intactes. C'est une opération d'étiquetage : « je vous affirme que ces nombres sont déjà en 4326 ». Ne l'utilisez que lorsque la métadonnée est absente ou erronée mais que les coordonnées sont réellement dans ce SCR (par exemple, le chargement d'un ST_MakePoint brut dont le SRID est 0).

ST_Transform(geom, 32631) reprojette : il lit le SRID source, fait passer l'opération de coordonnées par PROJ et renvoie de nouvelles coordonnées dans le SCR cible. Utilisez-le chaque fois que vous devez réellement déplacer des données entre systèmes de coordonnées.

L'échec classique : les données arrivent en mètres UTM mais leur SRID n'est pas défini, et quelqu'un les « corrige » avec ST_SetSRID(geom, 4326). Les nombres valent toujours des centaines de milliers de mètres, désormais mal étiquetés comme des degrés de longitude, et chaque entité se retrouve placée dans le golfe de Guinée ou hors planète. La réparation correcte consiste à faire d'abord ST_SetSRID(geom, <srid_source_réel>) (pour l'étiqueter honnêtement), puis ST_Transform si vous avez besoin d'un SCR différent.

Différences d'indexation et de performances

Les deux types s'indexent différemment, et cela tranche souvent le choix pour les tables à fort volume.

geometry comme geography utilisent des index spatiaux GiST (ou SP-GiST) construits sur des boîtes englobantes, créés de la même manière :

CREATE INDEX idx_parcels_geom ON parcels USING gist (geom);

Pour geometry, la boîte englobante est un simple rectangle planaire 2D, et les recherches d'index sont rapides et bien comprises. Pour geography, PostGIS calcule des boîtes englobantes sur la sphère, ce qui gère correctement les pôles et l'antiméridien mais est plus coûteux à évaluer. En pratique, les requêtes planaires sur geometry dans un SCR projeté sont généralement l'option la plus rapide, raison pour laquelle les analystes traitant des millions d'entités à l'intérieur d'une seule région transforment habituellement vers une colonne geometry projetée et y restent.

Un deuxième levier de performance est le drapeau use_spheroid des fonctions geography. ST_Distance(a, b) sur geography utilise par défaut le calcul sphéroïdal (plus lent, plus précis) ; passer false bascule vers une approximation sphérique plus rapide et généralement à environ 0,3 % de la réponse sphéroïdale, ce qui convient à de nombreuses tâches de proximité :

SELECT ST_Distance(a::geography, b::geography, false) AS dist_m;

Pour le motif courant « y a-t-il quelque chose à moins de X mètres », préférez ST_DWithin au calcul de toutes les distances suivi d'un filtrage, car ST_DWithin s'appuie sur l'index et court-circuite, tandis que ST_Distance dans une clause WHERE force un balayage complet.

Pièges courants et leurs causes

  • Distances décimales minuscules. ST_Distance sur une geometry 4326 renvoie des degrés parce que le calcul est planaire dans les unités de la colonne. La cause est de traiter des coordonnées angulaires comme s'il s'agissait d'un plan métrique. Convertissez en geography ou transformez vers un SCR projeté.
  • ST_Area sur une geometry lon/lat est absurde. Une surface en « degrés carrés » n'a aucune signification physique, et un degré de longitude rétrécit vers les pôles. Utilisez ST_Area(geom::geography) pour des mètres carrés, ou transformez vers une projection équivalente.
  • Mélanger des SRID dans un même appel de fonction. PostGIS lève Operation on mixed SRID geometries pour de nombreuses fonctions, mais les prédicats spatiaux peuvent aussi simplement renvoyer des résultats inattendus. Imposez un seul SRID par colonne avec un typmod comme geometry(Point, 4326).
  • Supposer que geography prend tout en charge. Appeler une fonction constructive non prise en charge sur geography lève soit une erreur, soit retombe sur un raccourci planaire. Vérifiez la prise en charge geography dans la référence PostGIS avant de vous y fier.
  • Franchissements de l'antiméridien et des pôles sur une geometry projetée. Une seule zone UTM ne peut pas représenter des entités qui s'enroulent autour du 180e méridien ; geography les gère nativement.

Contrôle qualité et validation

Avant de faire confiance à un résultat spatial, exécutez quelques vérifications peu coûteuses :

  • Confirmez que le SRID déclaré de la colonne correspond aux plages de coordonnées réelles. Les valeurs lon/lat tombent à peu près dans [-180, 180] et [-90, 90] ; les abscisses (eastings) UTM se situent autour de 100 000 à 900 000. SELECT DISTINCT ST_SRID(geom) FROM sites; plus un contrôle d'emprise avec ST_Extent(geom) détecte la plupart des erreurs d'étiquetage.
  • Validez les géométries avec ST_IsValid(geom) et localisez les problèmes avec ST_IsValidReason ; réparez avec ST_MakeValid.
  • Vérifiez par sondage une ou deux distances connues face à une mesure indépendante avant d'accepter un résultat de lot.
  • Pour la surface, comparez un résultat issu d'une conversion geography à une transformation vers un SCR équivalent local ; un désaccord important signale un problème de SCR en amont.

Le point de vue de Bathyl

Nous adoptons par défaut une colonne canonique geometry(<type>, 4326) avec un index fonctionnel geography, puis nous transformons vers un SCR projeté régional pour les analyses nécessitant des zones tampons, des superpositions ou une vitesse maximale. La question décisive pour chaque table n'est pas « quel type est meilleur » mais « quelle est la plus grande emprise que couvrira une requête, et quelles fonctions doivent s'y exécuter ». Répondez d'abord à cela, et le choix geometry vs geography se fait généralement de lui-même.

Pour aller plus loin

Sources