Réponse courte

Un index spatial est un arbre de boîtes englobantes d'entités qui permet à un moteur SIG de répondre à « qu'y a-t-il à proximité ? » sans lire chaque entité. Sans lui, une requête comme « donne-moi les parcelles intersectant cette vue cartographique » force un balayage séquentiel : le moteur teste la géométrie des 2 millions de parcelles même si 50 sont pertinentes. Avec un index R-tree ou GiST, il restreint d'abord aux quelques centaines dont les boîtes englobantes recoupent la vue, puis n'exécute le coûteux test géométrique exact que sur celles-là. Sur de grandes couches, c'est la différence entre des millisecondes et des minutes.

Différents conteneurs implémentent cela différemment — un .qix annexe pour les shapefiles, un R-tree interne pour le GeoPackage, un index GiST dans PostGIS — mais le principe est identique et le mode de défaillance est identique : un index manquant ou périmé fait ramper les requêtes spatiales.

Comment fonctionne réellement un index spatial

Les géométries sont arbitrairement complexes — un polygone de littoral peut avoir 100 000 sommets. Tester si deux tels polygones s'intersectent est coûteux en calcul. Un index spatial contourne l'essentiel de ce travail avec une stratégie en deux phases :

  1. Filtrer (l'index). Chaque entité est réduite à son rectangle englobant minimal (MBR) — quatre nombres. Ces rectangles sont organisés en un arbre hiérarchique (un R-tree : des rectangles regroupés en rectangles parents, récursivement). Pour trouver les entités proches d'une fenêtre de requête, le moteur parcourt l'arbre, élaguant des branches entières dont le rectangle parent ne peut recouvrir la fenêtre. Cela renvoie un petit ensemble de candidats.
  2. Affiner (la géométrie). Ce n'est que pour les candidats que le moteur exécute le prédicat exact (ST_Intersects, ST_Contains, etc.) sur la géométrie complète.

L'index ne donne jamais la réponse finale à lui seul — il fournit une présélection peu coûteuse. C'est pourquoi « l'index n'est que des boîtes englobantes » n'est pas une faiblesse ; c'est tout l'intérêt.

PostGIS : les index GiST

PostGIS construit les index spatiaux à l'aide du cadre GiST (Generalized Search Tree) de PostgreSQL, qui implémente un R-tree sur les boîtes englobantes de géométrie.

CREATE INDEX idx_parcels_geom ON parcels USING GIST (geom);
ANALYZE parcels;

L'étape ANALYZE n'est pas facultative : le planificateur de requêtes a besoin de statistiques à jour pour choisir l'index plutôt qu'un balayage séquentiel. Après un chargement massif, analysez toujours.

Pour confirmer que l'index est utilisé, enveloppez la requête dans EXPLAIN ANALYZE :

EXPLAIN ANALYZE
SELECT id FROM parcels
WHERE ST_Intersects(geom, ST_MakeEnvelope(500000, 4600000, 501000, 4601000, 32630));

Cherchez Index Scan using idx_parcels_geom ou Bitmap Index Scan. Si vous voyez Seq Scan, l'index est manquant, périmé, ou le prédicat n'est pas indexable (une cause fréquente : envelopper geom dans une fonction comme ST_Buffer(geom, ...) du côté gauche de la comparaison, ce qui annule l'index). Pour de très grandes tables, envisagez aussi les index BRIN lorsque les données sont triées spatialement, et rappelez-vous que les index GiST utilisent la boîte englobante, donc le SRID doit correspondre entre la colonne et l'enveloppe de requête.

GeoPackage : R-tree interne

GeoPackage est une norme OGC bâtie sur un conteneur SQLite. Son index spatial est un R-tree implémenté sous forme de tables virtuelles SQLite (le module rtree), stocké à l'intérieur du fichier .gpkg aux côtés de déclencheurs qui maintiennent l'index synchronisé à mesure que les entités changent. C'est un véritable avantage sur les shapefiles : l'index voyage avec les données dans un fichier unique.

GDAL crée le R-tree par défaut lorsque vous écrivez un GeoPackage. Vous pouvez en ajouter un explicitement avec la fonction d'extension GeoPackage gpkgAddSpatialIndex, et vous pouvez vérifier son existence en interrogeant les métadonnées :

SELECT * FROM gpkg_extensions WHERE extension_name = 'gpkg_rtree_index';

Lorsque vous convertissez en GeoPackage avec ogr2ogr, l'index spatial est construit automatiquement ; pour être explicite :

ogr2ogr -f GPKG out.gpkg in.shp -lco SPATIAL_INDEX=YES

Shapefiles : fichiers d'index annexes

Un shapefile est un ensemble de fichiers frères (.shp géométrie, .shx index de formes, .dbf attributs, .prj SCR). Le .shx n'est pas un index spatial — c'est un index de décalage d'enregistrements permettant un accès rapide par numéro d'enregistrement. L'index spatial réel est un fichier annexe distinct :

  • .qix — l'index quadtree ouvert utilisé par GDAL/OGR, QGIS et MapServer.
  • .sbn/.sbx — l'index spatial propriétaire d'Esri.

Sans un .qix (ou .sbn), chaque requête spatiale sur le shapefile balaie tout le .shp. Construisez un .qix avec GDAL :

ogrinfo -sql "CREATE SPATIAL INDEX ON parcels" parcels.shp

Dans QGIS, c'est Propriétés de la couche → Source → Créer un index spatial. Comme l'index est un fichier distinct, il est facile à perdre : copiez uniquement .shp/.shx/.dbf/.prj dans un nouveau dossier et l'index a disparu, et un shapefile édité peut laisser un .qix périmé qui ne correspond plus à la géométrie. Cette fragilité est l'un des arguments pratiques les plus forts en faveur de GeoPackage plutôt que de shapefile.

GeoJSON et autres formats texte

GeoJSON (RFC 7946, longitude-latitude WGS84) n'a aucun index spatial intégré. C'est un flux de texte brut, donc toute requête charge et analyse l'intégralité du fichier. C'est acceptable pour quelques milliers d'entités sur une carte web, mais un mauvais choix pour des jeux de données analytiques ou volumineux — ceux-ci ont leur place dans GeoPackage, PostGIS ou un format tuilé (tuiles vectorielles, FlatGeobuf, qui porte un R-tree compacté optionnel). Si vous devez interroger de façon répétée un GeoJSON volumineux, chargez-le d'abord dans PostGIS ou convertissez-le en GeoPackage.

Exemple détaillé : pourquoi cela compte

Une couche de parcelles de 1,8 million d'entités, interrogeant les parcelles d'un seul pâté de maisons :

  • GeoJSON, sans index : analyse des 1,8 M d'entités, ~dizaines de secondes, mémoire élevée.
  • Shapefile, sans .qix : balayage séquentiel du .shp, plusieurs secondes.
  • Shapefile avec .qix : le quadtree restreint à quelques centaines de candidats, moins d'une seconde.
  • GeoPackage avec R-tree : filtre R-tree à l'intérieur de SQLite, moins d'une seconde, fichier portable unique.
  • PostGIS avec GiST + ANALYZE : Bitmap Index Scan, millisecondes, et passage à l'échelle pour des utilisateurs concurrents.

La géométrie n'a jamais changé. Seul le chemin d'accès l'a fait.

Assurance qualité et validation avant remise

  • PostGIS : confirmez que CREATE INDEX ... USING GIST existe (\d table dans psql), que vous avez exécuté ANALYZE, et que EXPLAIN ANALYZE montre un balayage d'index sur des requêtes réelles.
  • GeoPackage : confirmez que les tables virtuelles rtree_* existent et que gpkg_extensions répertorie gpkg_rtree_index.
  • Shapefile : confirmez qu'un .qix se trouve à côté du .shp, et qu'il a été reconstruit après la dernière édition (un index périmé renvoie silencieusement de mauvais candidats).
  • Faites correspondre les SRID : un index sur une géométrie EPSG:32630 interrogé avec une enveloppe 4326 ne renvoie rien d'utile — confirmez que le SCR de la requête correspond à celui de la colonne.

Pièges courants et leurs causes

  • .qix périmé ou manquant. Des shapefiles ont été copiés sans le fichier annexe, ou édités sans reconstruction. La requête « fonctionne » mais est lente, ou renvoie des candidats périmés. Reconstruisez-le.
  • Oublier ANALYZE dans PostGIS. L'index existe mais le planificateur, faute de statistiques, choisit encore un balayage séquentiel. Exécutez ANALYZE.
  • Fonctions sur la colonne indexée. WHERE ST_Buffer(geom, 10) && ... ne peut pas utiliser l'index GiST ; tamponnez plutôt la géométrie de requête, en laissant geom brut à gauche.
  • Indexer du GeoJSON. Il ne peut pas être indexé sur place ; migrez vers un conteneur indexé.
  • Supposer que .shx est l'index spatial. Ce n'est pas le cas. Il vous faut quand même .qix.

Le point de vue de Bathyl

Nous considérons l'index spatial comme une partie du livrable, et non comme un détail interne, car une couche non indexée qui s'ouvre sans problème sur le bureau s'effondre dès qu'elle alimente une carte web ou une base de données multi-utilisateurs. Choisir GeoPackage ou PostGIS plutôt que des shapefiles épars est en grande partie une décision de garder l'index avec les données pour qu'il ne puisse pas se perdre en transit.

À lire aussi chez Bathyl

Sources