Bases de données spatiales et PostGIS#
Note : Les requêtes SQL PostGIS de ce chapitre supposent une connexion à un serveur PostgreSQL avec l’extension PostGIS installée. Ils sont présentés comme blocs illustratifs. Les cellules Python exécutables utilisent la bibliothèque
shapelypour les calculs et visualisations géométriques.
Données géographiques : les types fondamentaux#
Les données spatiales représentent des entités du monde réel par leur forme géométrique et leur position dans l’espace.
Définition 111
Les trois types géométriques fondamentaux en SQL spatial sont :
Point : une paire de coordonnées (x, y) ou (longitude, latitude). Représente un lieu précis : une ville, un capteur, une adresse.
LineString : une séquence ordonnée de points reliés par des segments. Représente une route, une rivière, un tracé de frontière.
Polygon : une région fermée délimitée par un anneau extérieur et éventuellement des anneaux intérieurs (trous). Représente un pays, un quartier, un bâtiment.
Ces types de base sont combinés en types multiples : MultiPoint, MultiLineString, MultiPolygon, et GeometryCollection.
WKT et WKB#
Définition 112
Deux formats standards d’échange des géométries :
WKT (Well-Known Text) : représentation textuelle lisible par un humain. Exemples :
POINT(2.3522 48.8566),LINESTRING(0 0, 1 1, 2 0),POLYGON((0 0, 4 0, 4 4, 0 4, 0 0)).WKB (Well-Known Binary) : représentation binaire compacte, utilisée pour le stockage et les échanges efficaces entre systèmes. Non lisible directement, mais interopérable entre tous les SIG.
Systèmes de coordonnées : SRID et projections#
Définition 113
Un système de référence spatiale (SRS) définit comment les coordonnées d’une géométrie correspondent à des positions sur la Terre. Il est identifié par un SRID (Spatial Reference ID), un entier issu du registre EPSG.
Les deux SRID les plus courants :
EPSG:4326 (WGS 84) : système géographique utilisé par le GPS. Les coordonnées sont en degrés décimaux (longitude, latitude). C’est le standard pour stocker des données géographiques mondiales.
EPSG:3857 (Web Mercator) : projection utilisée par Google Maps, OpenStreetMap. Les coordonnées sont en mètres. Pratique pour l’affichage, mais distord les surfaces aux hautes latitudes.
Remarque 71
Le choix de la projection est crucial pour les calculs :
Calculer une distance en degrés (EPSG:4326) sans reprojection donne un résultat incorrect car les degrés n’ont pas la même longueur selon la latitude.
Pour des calculs de distances métriques exacts, il faut soit utiliser le type
GEOGRAPHYde PostGIS (calculs sur sphéroïde), soit reprojecter les données dans un système métrique local.
PostGIS : installation et types#
PostGIS est l’extension officielle de PostgreSQL pour les données spatiales. Elle ajoute des types, des fonctions et des index spéciaux.
Définition 114
PostGIS ajoute deux types principaux à PostgreSQL :
GEOMETRY: géométrie plane (coordonnées cartésiennes). Les calculs sont euclidiens. Plus rapide mais moins précis pour les longues distances sur la Terre.GEOGRAPHY: géométrie géodésique (coordonnées sphériques, défaut EPSG:4326). Les calculs (distance, aire) tiennent compte de la courbure de la Terre. Plus précis pour les données mondiales.
Installation et initialisation (blocs illustratifs — requiert PostGIS) :
-- Activer l'extension
CREATE EXTENSION IF NOT EXISTS postgis;
-- Vérifier la version
SELECT PostGIS_Version();
-- Créer une table spatiale
CREATE TABLE villes (
id SERIAL PRIMARY KEY,
nom TEXT NOT NULL,
pays TEXT,
geom GEOMETRY(POINT, 4326) -- Point en WGS 84
);
-- Insertion via WKT et ST_GeomFromText
INSERT INTO villes (nom, pays, geom) VALUES
('Paris', 'France', ST_GeomFromText('POINT(2.3522 48.8566)', 4326)),
('Lyon', 'France', ST_GeomFromText('POINT(4.8357 45.7640)', 4326)),
('Marseille','France', ST_GeomFromText('POINT(5.3698 43.2965)', 4326)),
('Genève', 'Suisse', ST_GeomFromText('POINT(6.1432 46.2044)', 4326)),
('Madrid', 'Espagne', ST_GeomFromText('POINT(-3.7038 40.4168)', 4326));
-- Vue de la géométrie en WKT
SELECT nom, ST_AsText(geom) FROM villes;
Fonctions spatiales essentielles#
Définition 115
Les fonctions spatiales PostGIS les plus utilisées :
Fonction |
Description |
|---|---|
|
Distance entre deux géométries |
|
|
|
Les géométries se croisent-elles ? |
|
Aire d’un polygone |
|
Tampon de rayon |
|
Reprojeter dans un autre système |
|
Union de deux géométries |
|
Centre géométrique |
|
Longueur d’une ligne |
|
|
Requêtes spatiales illustratives (PostGIS)#
-- Distance entre Paris et Lyon (en degrés, GEOMETRY)
SELECT ST_Distance(
ST_GeomFromText('POINT(2.3522 48.8566)', 4326),
ST_GeomFromText('POINT(4.8357 45.7640)', 4326)
) AS distance_degres;
-- Distance réelle en mètres avec le type GEOGRAPHY
SELECT ST_Distance(
ST_GeographyFromText('POINT(2.3522 48.8566)'),
ST_GeographyFromText('POINT(4.8357 45.7640)')
) AS distance_metres;
-- Villes dans un rayon de 500 km autour de Paris
SELECT v.nom,
ROUND(ST_Distance(
v.geom::geography,
ST_GeographyFromText('POINT(2.3522 48.8566)')
) / 1000) AS distance_km
FROM villes v
WHERE ST_DWithin(
v.geom::geography,
ST_GeographyFromText('POINT(2.3522 48.8566)'),
500000 -- 500 km en mètres
)
ORDER BY distance_km;
-- Tampon de 50 km autour de Paris (en projection métrique)
SELECT ST_Buffer(
ST_Transform(ST_GeomFromText('POINT(2.3522 48.8566)', 4326), 2154),
50000 -- 50 km en mètres (EPSG:2154 = Lambert 93, projection française)
) AS zone_tampon;
Index spatial GiST#
Définition 116
PostGIS utilise un index GiST (Generalized Search Tree) basé sur un R-tree pour accélérer les requêtes spatiales. Un R-tree décompose l’espace en rectangles minimaux englobants (MBR — Minimum Bounding Rectangle). Sans index, toute requête spatiale nécessite un parcours complet de la table (seq scan).
-- Créer un index GiST sur la colonne géométrique
CREATE INDEX idx_villes_geom ON villes USING GIST (geom);
-- L'index est utilisé automatiquement par les opérateurs &&, @>, etc.
-- Les fonctions ST_DWithin, ST_Intersects, ST_Contains bénéficient de l'index.
EXPLAIN ANALYZE
SELECT nom FROM villes
WHERE ST_DWithin(geom::geography,
ST_GeographyFromText('POINT(2.3522 48.8566)'),
300000);
Remarque 72
Le R-tree supporte aussi la recherche du plus proche voisin (k-NN search) efficacement :
-- Les 3 villes les plus proches de Paris
SELECT nom,
ST_Distance(geom::geography,
ST_GeographyFromText('POINT(2.3522 48.8566)')) / 1000
AS distance_km
FROM villes
ORDER BY geom::geography <->
ST_GeographyFromText('POINT(2.3522 48.8566)')
LIMIT 3;
L’opérateur <-> calcule la distance en utilisant l’index GiST — c’est beaucoup plus rapide que ORDER BY ST_Distance(...) sans index.
Requêtes spatiales avancées#
Exemple 39
Trouver les points dans un polygone — cas d’usage typique : quels sites sont dans une zone administrative ?
-- Villes dans un polygone définissant approximativement la France métropolitaine
SELECT nom
FROM villes
WHERE ST_Contains(
ST_GeomFromText('POLYGON((
-5 42, 9 42, 9 51, -5 51, -5 42
))', 4326),
geom
);
Exemple 40
Trouver les routes qui intersectent une zone :
CREATE TABLE routes (
id SERIAL PRIMARY KEY,
nom TEXT,
geom GEOMETRY(LINESTRING, 4326)
);
-- Routes qui traversent le département de l'Hérault
SELECT r.nom
FROM routes r
JOIN departements d ON ST_Intersects(r.geom, d.geom)
WHERE d.code = '34';
Calculs avec shapely (Python exécutable)#
# Création de géométries avec shapely
paris = Point(2.3522, 48.8566)
lyon = Point(4.8357, 45.7640)
marseille = Point(5.3698, 43.2965)
geneve = Point(6.1432, 46.2044)
madrid = Point(-3.7038, 40.4168)
villes = {
"Paris": paris,
"Lyon": lyon,
"Marseille": marseille,
"Genève": geneve,
"Madrid": madrid,
}
# Distance euclidienne (en degrés) — illustrative
print("Distances depuis Paris (degrés, euclidien) :")
for nom, pt in villes.items():
if nom != "Paris":
d = paris.distance(pt)
print(f" Paris → {nom:<12} : {d:.4f}°")
Distances depuis Paris (degrés, euclidien) :
Paris → Lyon : 3.9664°
Paris → Marseille : 6.3262°
Paris → Genève : 4.6266°
Paris → Madrid : 10.3878°
# Conversion approx. degrés → km (1° ≈ 111 km)
print("Distances approx. depuis Paris (km) :")
for nom, pt in villes.items():
if nom != "Paris":
d_deg = paris.distance(pt)
d_km = d_deg * 111
print(f" Paris → {nom:<12} : {d_km:.0f} km")
Distances approx. depuis Paris (km) :
Paris → Lyon : 440 km
Paris → Marseille : 702 km
Paris → Genève : 514 km
Paris → Madrid : 1153 km
# Création d'une zone tampon (buffer) autour de Paris
# En coordonnées géographiques, 1° ≈ 111 km
rayon_3deg = 3.0 # ~333 km
zone_paris = paris.buffer(rayon_3deg)
# Villes dans la zone
print("Villes dans un rayon de ~333 km autour de Paris :")
for nom, pt in villes.items():
if zone_paris.contains(pt):
print(f" ✓ {nom}")
else:
print(f" ✗ {nom}")
Villes dans un rayon de ~333 km autour de Paris :
✓ Paris
✗ Lyon
✗ Marseille
✗ Genève
✗ Madrid
# Polygone représentant approximativement la France métropolitaine
france_approx = Polygon([
(-5, 42), (9, 42), (9, 51), (-5, 51), (-5, 42)
])
print("Villes dans l'approximation de la France métropolitaine :")
for nom, pt in villes.items():
print(f" {'✓' if france_approx.contains(pt) else '✗'} {nom}")
Villes dans l'approximation de la France métropolitaine :
✓ Paris
✓ Lyon
✓ Marseille
✓ Genève
✗ Madrid
# Route entre Paris et Marseille
route_pm = LineString([paris, lyon, marseille])
print(f"Longueur de la route Paris→Lyon→Marseille : {route_pm.length:.4f}° "
f"(≈ {route_pm.length * 111:.0f} km)")
# Intersection route et zone tampon
intersection = route_pm.intersection(zone_paris)
print(f"Longueur de la route dans la zone Paris : {intersection.length * 111:.0f} km")
Longueur de la route Paris→Lyon→Marseille : 6.4910° (≈ 721 km)
Longueur de la route dans la zone Paris : 333 km
Visualisation : géométries avec matplotlib#
# Tableau récapitulatif des opérations spatiales
poly_a = Polygon([(0,0),(4,0),(4,3),(0,3),(0,0)])
poly_b = Polygon([(2,1),(6,1),(6,4),(2,4),(2,1)])
resultats = {
"ST_Area(A)": poly_a.area,
"ST_Area(B)": poly_b.area,
"ST_Area(A ∩ B)": poly_a.intersection(poly_b).area,
"ST_Area(A ∪ B)": poly_a.union(poly_b).area,
"ST_Length(A → centre B)": LineString([poly_a.centroid, poly_b.centroid]).length,
"ST_Intersects(A, B)": poly_a.intersects(poly_b),
"ST_Contains(A, B)": poly_a.contains(poly_b),
"ST_Distance(A, B)": poly_a.distance(poly_b),
}
pd.DataFrame.from_dict(resultats, orient='index', columns=['Valeur'])
| Valeur | |
|---|---|
| ST_Area(A) | 12.0 |
| ST_Area(B) | 12.0 |
| ST_Area(A ∩ B) | 4.0 |
| ST_Area(A ∪ B) | 20.0 |
| ST_Length(A → centre B) | 2.236068 |
| ST_Intersects(A, B) | True |
| ST_Contains(A, B) | False |
| ST_Distance(A, B) | 0.0 |
Cas d’usage : villes dans un rayon, intersections#
Exemple 41
Un cas d’usage courant est la zone d’isochrone : délimiter la zone accessible en moins de N minutes depuis un point. Les outils comme Valhalla ou OSRM calculent ces polygones, qui sont ensuite stockés dans PostGIS pour des requêtes du type « quels clients habitent à moins de 30 minutes de notre magasin ? ».
-- Supposons que la table isochrones contienne des polygones préalculés
SELECT c.nom, c.adresse
FROM clients c
JOIN isochrones i ON ST_Within(c.geom, i.geom)
WHERE i.magasin_id = 42
AND i.minutes = 30;
Remarque 73
Applications typiques des bases de données spatiales :
Urbanisme : trouver tous les bâtiments dans une zone inondable.
Logistique : optimiser les tournées de livraison, calculer les zones de chalandise.
Environnement : superposer des données climatiques et des aires protégées.
Télécommunications : calculer les zones de couverture des antennes.
Assurance : évaluer l’exposition au risque selon la localisation.
Résumé#
Remarque 74
Ce chapitre a introduit les bases de données spatiales et PostGIS :
Fondements :
Trois types géométriques fondamentaux :
POINT,LINESTRING,POLYGON, et leurs variantes multiples.WKT (texte lisible) et WKB (binaire) sont les formats d’échange standards.
Le SRID identifie le système de référence spatiale (EPSG:4326 = WGS 84 pour le GPS).
PostGIS :
GEOMETRYpour les calculs plans,GEOGRAPHYpour les calculs géodésiques précis.Les fonctions spatiales (
ST_Distance,ST_Contains,ST_Intersects,ST_Buffer,ST_Transform) couvrent la quasi-totalité des besoins analytiques.L’index
GiST(R-tree) est indispensable pour les performances sur les grandes tables spatiales.L’opérateur
<->permet la recherche efficace du plus proche voisin via l’index.
Python avec shapely :
shapelypermet de créer et manipuler des géométries sans serveur de base de données.Les opérations booléennes (
intersection,union,difference), les mesures (area,length,distance) et les prédicats (contains,intersects) sont directement disponibles.