Utiliser les topologies PostGIS pour nettoyer un filaire de voirie

Cet article présente les bases pour démarrer avec l'utilisation de l'extension topology de PostGIS.

Le blog Makina-corpus

Cet article présente les bases pour démarrer avec l'utilisation de l'extension topology de PostGIS.

Dans Geotrek, les objets géométriques sont décrits à partir d'un réseau topologique de tronçons, en utilisant la segmentation dynamique. Lors de l'import initial de la BD Topo® IGN ou des filaires de voiries spécifiques, nous procédons à un nettoyage topologique des données.

Cet article présente les bases pour démarrer avec l'utilisation de l'extension topology de PostGIS.

Nous allons tirer parti des topologies pour nettoyer un réseau réel : le filaire de voirie mis à disposition sur le portail OpenData du Grand Toulouse.


Topologique

Une topologie est un concept très général, où les objets sont définis par leurs relations plutôt que par leurs géométries. Au lieu des lignes, on manipule des arrêtes, des noeuds et des faces : cela doit vous rappeler les fondamentaux de la théorie des graphes.

Un réseau topologique est supposé avoir ses lignes (arrêtes) connectés à des points uniques (noeuds).

Dans ce jeu de données, le validateur JOSM détecte pas moins de 1643 erreurs :) Connexions cassées, lignes qui se croisent ...

Aller, nettoyons tout ça !


Installation

Sur Ubuntu 12.04, il suffit d'installer PostGIS :

sudo apt-add-repository -y ppa:ubuntugis/ppa sudo apt-get update sudo apt-get install -y postgresql postgis

L'extension topologique est installée par défaut. On l'active juste dans la base :

CREATE DATABASE "roadsdb"; CREATE EXTENSION postgis; CREATE EXTENSION postgis_topology; SET search_path = topology,public;


Import des données

Charger le shapefile (avec la ligne de commande) comme d'habitude :

schema="public." db="roadsdb" user="postgres" password="postgres" host="localhost" ogr2ogr -f "PostgreSQL" PG:"host=${host} user=${user} dbname=${db} password=${password}" -a_srs "EPSG:2154" -nln ${schema}roads -nlt MULTILINESTRING ROAD_SHAPEFILE.SHP

Créer et associer la topologie PostGIS:

SELECT topology.CreateTopology('roads_topo', 2154); SELECT topology.AddTopoGeometryColumn('roads_topo', 'public', 'roads', 'topo_geom', 'LINESTRING');

Convertir les lignes brisées en noeuds et arrêtes au sein de la topologie :

-- Layer 1, avec une tolérance de 1.0 mètre UPDATE roads SET topo_geom = topology.toTopoGeom(wkb_geometry, 'roads_topo', 1, 1.0);

À partir de maintenant, nous avons un réseau topologique, où les imperfections ont été corrigées. Il a fusionné les jonctions imparfaites, où les défauts font moins d'un mètre de large.

( en vert, version corrigée )

Il est possible de rencontrer des problèmes à l'insertion : l'outil d'import échoue [#]_ et abandonne alors la transaction complète.

On peut utiliser ce bout de code pour passer outre les erreurs et poursuivre avec les enregistrements suivants:

DO $$DECLARE r record; BEGIN FOR r IN SELECT * FROM roads LOOP BEGIN UPDATE roads SET topo_geom = topology.toTopoGeom(wkb_geometry, 'roads_topo', 1, 1.0) WHERE ogc_fid = r.ogc_fid; EXCEPTION WHEN OTHERS THEN RAISE WARNING 'Loading of record % failed: %', r.ogc_fid, SQLERRM; END; END LOOP; END$$;

C'est plutôt frustrant d'avoir des erreurs topologiques à l'insertion ! Parfois, réduire la tolérance règle certains soucis. Il convient aussi de vérifier que les enregistrements ont des géométries valides. Toute clarification ou aide sur ce côté obscure du module topology sont les bienvenus :)


Visualiser et exporter

Pour visualiser les arrêtes de la topologie dans QGIS, parcourir la liste des tables, et ajouter les couches suivantes : roads_topo.edge_data and roads_topo.node.

On peut aussi exporter les géométries résultants (propres) vers une nouvelle table :

CREATE TABLE roads_clean AS ( SELECT ogc_fid, topo_geom::geometry FROM roads );

Ou récupérer notre Shapefile chéri :

ogr2ogr -f "ESRI Shapefile" ROAD_CLEAN.SHP PG:"host=${host} user=${user} dbname=${db} password=${password}" -sql "SELECT topo_geom::geometry FROM roads"

Si, comme Amit , on veut obtenir les lignes découpées aux intersections, tout en ajoutant les attributs de la table originale, on peut faire une jointure entre roads_topo.edge_data et la table roads:

SELECT r.lib_off, r.ogc_fid, e.geom FROM roads_topo.edge e, roads_topo.relation rel, roads r WHERE e.edge_id = rel.element_id AND rel.topogeo_id = (r.topo_geom).id


Aller plus loin...

Grace aux topologies, il a été possible de fusionner les lignes et de nettoyer les jonctions afin d'obtenir un réseau joli et propre.

Oui mais (hum hum), nous n'avons pas pu réparer automatiquement toutes les erreurs topologiques de ce jeu de données officiel. Certaines incohérences, comme celle ci-dessous, font 6 mètres de large ! À noter, que la même zone est décrite parfaitement dans OpenStreetMap :

On pourrait aussi jouer avec les simplifications topologiques présentées par Sandro Santilli SimplifyEdgeGeom [#]_, qui fusionnerait les arrêtes avec une tolérance itérative ...

SELECT SimplifyEdgeGeom('roads_topo', edge_id, 1.0) FROM roads_topo.edge;

N'hésitez pas à partager vos idées et retours. Les exemples et cas d'utilisations du module topology sont très rares! Contactez-nous sur twitter !

Inscription à la newsletter

Nous vous avons convaincus