Pages: 1
- Sujet précédent - [PostGis] Caler une LineString sur des tronçons d'un référentiel - Sujet suivant
#1 Mon 20 October 2014 11:59
- jerome_m
- Participant actif
- Lieu: Montpellier
- Date d'inscription: 28 Nov 2012
- Messages: 51
[PostGis] Caler une LineString sur des tronçons d'un référentiel
Mon étude :
Je possède une couche contenant une LineString (couche "Ligne") qui suit à peu près la couche route de la BD_Topo.
Je souhaite obtenir une autre couche, contenant une lineString et constitué de tronçons de la BD_Topo route (couche "route"). Pour les tronçons n'existant pas dans la BD_Topo, je pointerais vers une table ("troncon_perso") ayant la même structure que la table "route", mais avec des tronçons personnels que je créerai au besoin.
Voila comment je procède :
J'ai créé une table d'association ("liste_troncons") qui contient :
- un identifiant unique
- un identifiant d'un tronçon de la BD_Topo route
- un identifiant d'un tronçon de la couche "troncon_perso"
Je fait ensuite un buffer de x mètres autour de ma couche "ligne" et utilise ensuite la fonction whithin pour identifier les tronçons de la bd_topo qui sont à l'intérieur de ce buffer. Voici la commande PostGis:
Code:
-- Selection des tronçons de la ligne With troncon_ligne AS ( SELECT ogc_fid AS id_troncon_topo, ST_WITHIN( the_geom , (SELECT ST_BUFFER(the_geom,x) FROM test.ligne) AS within FROM test.route) -- Remplissage de la liste avec les troncon identifiés INSERT INTO test.liste_troncons(id_ligne,id_bd_topo) SELECT current.gid,id_troncon_topo FROM troncon_ligne WHERE within='t';
Je créé une vue pour voir les géométries liées à ma table associative, et je peux ainsi identifier les tronçons de la BD_Topo qui ne se superposent pas à ma couche ligne initiale. Je crée ainsi les tronçons manquant dans ma couche "troncon_perso" et rajoute les liens dans la table "liste_troncons".
Ma vue (basée sur la table associative et avec les jointures vers les tables liées) me permet donc de visualiser ma ligne, constituée désormais de tronçons de la BD_topo et de mes tronçons perso (dans le cas de manque dans la BD_topo).
Ma ligne étant composée de plusieurs entités, j'utilise la méthode suivante pour recréer une seule entité :
Code:
--Regroupement en une entité select st_astext(st_linemerge(st_collect(the_geom))) from test.troncons_ligne;
Mon problème :
Avec cette méthode, si ma ligne initiale est simple (j'entend par là qu'elle ne contient pas de chevauchement), je peux travailler dessus en mesurant sa longueur par exemple.
MAIS, dans le cas où ma ligne comprend le même tronçon plusieurs fois, la longueur va être fausse... Le tronçon ne sera présent qu'une seule fois dans ma table associative.
Mes interrogations :
La méthode utilisée permet de sélectionner les tronçons BD_topo de ma ligne initiale, mais ne crée pas une LineString correcte : j'entends par là que si ma ligne était A B C B D, j'aimerais obtenir A' B' C' B' D', les nouveaux points étant calés sur les intersections de la BD_topo.
La LineString initiale a été tracée dans un sens particulier, en passant par des points particuliers en faisant des boucles si nécessaires, et j'aimerais que ce "tracet" soit conservé.
Ma question :
Pouvez vous me conseiller une méthode, une piste à suivre, pour résoudre mon problème et obtenir l'équivalent de ma ligne initiale, mais constitué de tronçons de la BD_topo et de tronçons perso, le tout dans le bon ordre et en respectant le tracet initial.
J'espère avoir été clair ! N'hésitez pas à demander plus de renseignements si nécessaire.
Hors ligne
#2 Mon 20 October 2014 12:14
- Nicolas Ribot
- Membre
- Lieu: Toulouse
- Date d'inscription: 9 Sep 2005
- Messages: 1554
Re: [PostGis] Caler une LineString sur des tronçons d'un référentiel
Bonjour,
Une remarque à la lecture rapide: évitez de construire un buffer pour trouver tous les troncons de bd_topo situés à une certaine distance des lignes:
Postgis dispose de la fonction st_dwithin qui fait ca bcp, bcp plus vite:
Code:
-- Selection des tronçons de la ligne INSERT INTO test.liste_troncons(id_ligne,id_bd_topo) SELECT l.gid, r.ogc_fid FROM test.route r join test.ligne l on st_dwithin(r.geom, l.geom, x);
Nicolas
Hors ligne
#3 Mon 20 October 2014 14:09
- jerome_m
- Participant actif
- Lieu: Montpellier
- Date d'inscription: 28 Nov 2012
- Messages: 51
Re: [PostGis] Caler une LineString sur des tronçons d'un référentiel
Merci Nicolas pour cette remarque. En effet, l'utilisation de st_dwithin simplifie grandement la première requette et va m'être bien utile ! Très intéressant.
Par contre, concernant la méthodologie et mes interrogations, cela ne change rien au résultat obtenu.
Dernière modification par jerome_m (Thu 23 October 2014 10:54)
Hors ligne
#4 Mon 20 October 2014 17:07
- Nicolas Ribot
- Membre
- Lieu: Toulouse
- Date d'inscription: 9 Sep 2005
- Messages: 1554
Re: [PostGis] Caler une LineString sur des tronçons d'un référentiel
Vous auriez un echantillon de données ou une ligne exemple ?
Nico
Hors ligne
#5 Mon 20 October 2014 17:17
- Nicolas Ribot
- Membre
- Lieu: Toulouse
- Date d'inscription: 9 Sep 2005
- Messages: 1554
Re: [PostGis] Caler une LineString sur des tronçons d'un référentiel
Une question:
Comment créez-vous les tronçons manquants ? par dessin ou par jointure entre les segments consécutifs ?
Vous pouvez choisir, pour chaque troncon de ligne ordonné, le segment de route le plus proche.
Ensuite, vous reconstruisez la ligne finale, basée sur bd_topo, order by troncon_ligne.
Nico
Hors ligne
#6 Tue 21 October 2014 09:07
- jerome_m
- Participant actif
- Lieu: Montpellier
- Date d'inscription: 28 Nov 2012
- Messages: 51
Re: [PostGis] Caler une LineString sur des tronçons d'un référentiel
Pour les tronçons manquant, je les crée par dessin dans Qgis, avec l'option d'accrochage sur la couche de la BD_Topo d'activée (ainsi, je garantie une superposition des point de départ et d'arrivée avec la BD_Topo).
Quand vous dites : "Vous pouvez choisir, pour chaque troncon de ligne ordonné, le segment de route le plus proche." : à quelle fonction de Postgis faites vous référence ? Cette solution me parait tout à fait correspondre à mon besoin. Il faudra ensuite que je gère en plus les troncons perso pour les intégrer au milieu mais comme il n'y en a quasiment pas, cela ne devrait pas être trop difficile.
Hors ligne
#7 Tue 21 October 2014 09:53
- Nicolas Ribot
- Membre
- Lieu: Toulouse
- Date d'inscription: 9 Sep 2005
- Messages: 1554
Re: [PostGis] Caler une LineString sur des tronçons d'un référentiel
Bonjour,
Postgis sait aussi faire de l'accrochage auto entre geometries, dans certaines conditions.
La methode est fonction du type de données sur lesquelles vous travaillez, d'ou ma question sur un echantillon test:
Sont-ce des linestring simples, des multi, comment se recoupent ou se chevauchent-elles ?
Vous pouvez nous envoyer qq données, eventuellement anonymisées si nécessaire ?
Nico
Hors ligne
#8 Tue 21 October 2014 10:13
- jerome_m
- Participant actif
- Lieu: Montpellier
- Date d'inscription: 28 Nov 2012
- Messages: 51
Re: [PostGis] Caler une LineString sur des tronçons d'un référentiel
Merci Nicolas,
Les lignes sont des Linestring simple, tracées en une seule fois sans bifurcation ou autre, et dans le cas où elles passent deux fois au même endroit, il y a un chevauchement (par exemple, on relie les points A B C D E C B F).
Je vais préparer un set de données dans la journée ou demain.
Bonne journée
Hors ligne
#9 Tue 21 October 2014 13:04
- ppluvinet
- Participant assidu
- Lieu: VALENCE
- Date d'inscription: 6 Aug 2007
- Messages: 617
Re: [PostGis] Caler une LineString sur des tronçons d'un référentiel
C'est un problème qui revient assez souvent : comment faire pour "coller" deux couches qui vues de loin se superposent mais, en fait, ne se supperposent pas du tout; et où l'une est + précise, l'autre contient des info intéressantes....
Ca fait également penser à la BDTOPAGE qui tente de caler la BD CARTHAGE sur la BDTOPO. Ou à des pistes cyclables numérisées par un cycliste amateur qui ne se superpose pas vraiment avec le référentiel voirie de commune !
Dès lors qu'on souhaite faire des manip automatisée, il faut choisir des seuils : comment les définir ? ST_DWITHIN ( geom , ????) ?
Avec PostGIS, il y a comme le dit Nicolas, un tas de solutions, et ca dépend bien souvent du jeu de données, d'arriver à formuler tous les cas possibles, à éventuellement à créer un indice de 0 à 10 qui tente d'indiquer le % d'erreur à avoir caler une ligne sur la ligne référentielle (pour ensuite faire des corrections manuelles éventuelles).
Mon post ne fait pas vraiment avancer les choses mais la question m'intéresse !
Bon courage,
Dernière modification par ppluvinet (Thu 23 October 2014 18:59)
Pascal PLUVINET
Hors ligne
#10 Thu 23 October 2014 11:29
- jerome_m
- Participant actif
- Lieu: Montpellier
- Date d'inscription: 28 Nov 2012
- Messages: 51
Re: [PostGis] Caler une LineString sur des tronçons d'un référentiel
Bonjour à tous,
Voici un exemple de Linestring sur laquelle nous travaillons.
Avec la méthode utilisée dans le premier post, nous arrivons à identifier les tronçons de la BD_Topo corresondant, mais nous perdons l'ordre des tronçon et il n'est pas possible de reconstituer une LineString correcte.
Merci d'avance
Hors ligne
#11 Thu 23 October 2014 11:53
- Nicolas Ribot
- Membre
- Lieu: Toulouse
- Date d'inscription: 9 Sep 2005
- Messages: 1554
Re: [PostGis] Caler une LineString sur des tronçons d'un référentiel
Merci,
Vous auriez aussi les routes BD TOPO qui vont avec ?
Pour des tracés compliqués, comme le disait Pascal, ca peut vite devenir très complexe de coller à la couche de référence: que faire aux intersections, comment choisir le bon segment qd plusieurs segments de référence sont éligibles, etc.
Nicolas
Hors ligne
#12 Thu 23 October 2014 11:58
- Nicolas Granier
- Participant assidu
- Date d'inscription: 19 Apr 2007
- Messages: 271
Re: [PostGis] Caler une LineString sur des tronçons d'un référentiel
Bonjour,
si je comprends votre problème : lors d'un trajet si un véhicule emprunte deux fois la même rue, celle-ci n'est comptée en terme de distance qu'une seule fois, vous avez donc une distance erronée dans la longueur de l'itinéraire.
Votre problème n'est pas juste une identification du réseau BD Topo, mais un problème qui relève de la question des itinéraires et des réseaux.
Je pense qu'il y a aussi une autre donnée qui me parait indispensable dans ce traitement , ce sont les arrêts/points de passage des véhicules.
Une piste de réflexion (nécessitant un peu de programmation - python par exemple):
L'identification du réseau sur la bd topo et les ajouts manuels sont un bon départ.
Il faut aussi comme information une liste de points d'arrêts référencés par ordre de passage.
Avec pgrouting (module de gestion d'itinéraires de postgis) il est possible d'identifier le réseau mobilisé entre deux points (deux arrêts dans votre cas). Côté algorithme ça donnerait quelque chose comme cela:
1- On crée une table postgis vide qui va servir de conteneur.
2- On crée une boucle qui va identifié le réseau bd topo selectionné entre l'arret 1 et l'arret 2 (puis arret 2 vers arret 3 et ainsi de suite) avec pg routing
3- Le réseau BD Topo sélectionné est copié dans la table conteneur avec éventuellement un identifiant qui s'incrémente au fur à mesure des copies)
Ainsi on obtient une table contenant tous les tronçons de la bt topo participant à tous les itinéraires entre arrêts.
4- On crée une linestring de tous ces objets avec un st_collect afin de conserver les parties en double et des distances correctes
A+
Nicolas GRANIER
Hors ligne
#13 Thu 23 October 2014 12:29
- ppluvinet
- Participant assidu
- Lieu: VALENCE
- Date d'inscription: 6 Aug 2007
- Messages: 617
Re: [PostGis] Caler une LineString sur des tronçons d'un référentiel
La solution avec PgROUTING, je n'y avais pas pensé mais y'a de l'idée !
Pour des tracés compliqués, comme le disait Pascal, ca peut vite devenir très complexe de coller à la couche de référence: que faire aux intersections, comment choisir le bon segment qd plusieurs segments de référence sont éligibles, etc.
Une autre solution (à tester car je ne l'ai pas fait) pour tenter de repérer les erreurs de mauvais raccordement..
Vous découpez votre linestrind en tronçons de 50m (la distance est un seuil au pif donc à recaler peut-être) et vous numérotez bien les tronçons de 1 à N. Appelons ces toncons "TRONCLINE".
Puis vous créez sur chaque TRONCLINE, 10 points espacés de 5 m .
Puis vous cherchez pour chaque point le tronçons de BDTOPO le + proche.
Par agrégation vous pouvez connaitre le nombre de point qui se sont accrochés au même TRONCLINE. Cela donne un indice de confiance. Si j'ai 9/10 points qui se sont accrochés au même tronçon j'ai plus de chance que si j'en ai que 5. Si l'indice est faible, alors c'est que c'est le cas d'une intersection ou qu'il faut une reprise manuelle.
Sur Postgis, je pense que ca doit être un peu prise de tête mais carrément faisable.
Bon courage,
Dernière modification par ppluvinet (Thu 23 October 2014 18:58)
Pascal PLUVINET
Hors ligne
#14 Thu 23 October 2014 17:58
- jerome_m
- Participant actif
- Lieu: Montpellier
- Date d'inscription: 28 Nov 2012
- Messages: 51
Re: [PostGis] Caler une LineString sur des tronçons d'un référentiel
Voici un extrait du réseau routier sur lequel nous voudrions caler notre linestring.
Je tiens à vous remercier pour votre implication !
Hors ligne
#15 Fri 24 October 2014 10:12
- jerome_m
- Participant actif
- Lieu: Montpellier
- Date d'inscription: 28 Nov 2012
- Messages: 51
Re: [PostGis] Caler une LineString sur des tronçons d'un référentiel
@Nicolas Ribot : avec ST_dwithin(), si je met un buffer de recherche de quelques mètres, cela fonctionne très bien : en effet, mon tracé initial est très proche de ma couche de référence ce qui simplifie quand même cette étape. Pour les zones sans correspondance, comme précisé au début, je créerai un nouveau tronçon perso.
@Nicolas Granier : votre approche est très intéressante, et elle fait ressortir la notion d'itinéraire, reliant des points ordonnés par des tronçons de route. L'algorithme me semble correct, et si je comprends bien, il me suffirait de créer quelques points de passage pour "spécifier" le sens de parcours pour les zones litigieuse (boucles ou zones de passages dans les deux sens). Ensuite, il sera possible de créer une LineString correcte.
@Pascal PLUVINET : c'est une solution à tester dans le cas où le tracé initial est assez éloigné de la couche référence. Le gros point positif est l'indice de confiance qui permet de choisir dans le cas où plusieurs tronçons sont identifiés.
@tous: suite à ces discussions, je me pose une question concernant la visualisation des LineString et des itinéraires avec QGis. Est il possible de les visualiser en représentant le sens de passage et l'ordre des tronçons ? (avec des petites flèches par exemple, un peu comme dans les logiciels permettant de visualiser des itinéraires).
Merci à tous pour votre participation
Hors ligne
#16 Fri 24 October 2014 10:41
- ppluvinet
- Participant assidu
- Lieu: VALENCE
- Date d'inscription: 6 Aug 2007
- Messages: 617
Re: [PostGis] Caler une LineString sur des tronçons d'un référentiel
@tous: suite à ces discussions, je me pose une question concernant la visualisation des LineString et des itinéraires avec QGis. Est il possible de les visualiser en représentant le sens de passage et l'ordre des tronçons ? (avec des petites flèches par exemple, un peu comme dans les logiciels permettant de visualiser des itinéraires).
Oui c'est possible.
Une des solutions :
Dans les propriétés/Style , il faut ajouter un symbole avec le gros + vert.
Ci joint un exemple de qml (QGIS 2.4)
Pascal PLUVINET
Hors ligne
#17 Fri 24 October 2014 11:28
- Nicolas Ribot
- Membre
- Lieu: Toulouse
- Date d'inscription: 9 Sep 2005
- Messages: 1554
Re: [PostGis] Caler une LineString sur des tronçons d'un référentiel
Bonjour,
Merci pour les données routes: J'etais parti avec des données OSM, non calées sur la ligne => me suis embarqué dans de la reprojection de points pour d'abord caler la ligne sur OSM, puis ensuite retrouver l'ordre des troncons OSM: bien tricky mais faisable
En voyant vos données, on constate que la ligne a ete numérisée sur bd_topo => les sommets de la ligne sont déja calés sur bd_route, les deux tracés étant superposés exactement (sauf en un point ou il y a un décalage de 10cm).
On constate aussi que BD route est topologiquement correcte: segments individuels connectés aux intersections.
Du coup, le processus pour retrouver les segments BD TOPO dans le bon ordre et suivant la ligne est plus facile: on va se servir de cette caractéristique.
Au passage, je vous ai dit une "bêtise": dans votre cas, st_buffer + st_contains pour trouver toutes les routes qui suivent la ligne est la bonne méthode: st_dwithin avec deux lignes peut renvoyer des lignes proches mais pas completement incluses dans le buffer: ca génère trop de faux positifs
Le principe:
• Explosion de la ligne en segments individuels numérotés, a partir des sommets de la ligne: ca va servir a ordonner bd route à la fin:
=> table vertex (sommets de la ligne) et segments (2 points consécutifs dans la ligne)
• Petit Buffer de la ligne (1m par ex, les données étant calées) pour retrouver tous les segments BD Route + la ligne de départ + la ligne d'arrivée (qui ne sont pas contenues dans le buffer, mais qui l'intersectent, sauf si le trajet ligne commence au début d'une route BD Topo).
=> table route_tmp: les routes bd topo suivant la ligne
• Ensuite, on associe a chaque segment le troncon bd_route qui le contient: on a avoir des doublons: plusieurs segments de la ligne appartiennent parfois au meme troncon bd_route.
• Enfin, on utilise une CTE recursive pour identifier les segments bd routes successifs, non répétitifs, qui forment la ligne. Avec cette méthode, on ne detecte pas un demi tour fait sur le meme troncon BD Route, mais on détecte les boucles (plusieurs passages sur le meme troncon).
En SQL et en image:
creation de la table des sommets de la ligne, ordonnables par le champ idx
Code:
drop table IF EXISTS vertex; create table vertex as ( -- multilinestring => 2eme element de path a extraire pour avoir l'index du sommet de la ligne select gid, (st_dumppoints(geom)).path[2] as idx, (st_dumppoints(geom)).geom from ligne );
self join sur la table des vertex pour construire un segment entre les points n et n+1:
Code:
create table segments as ( select t.idx, st_collect(t.geom, t1.geom) as geom from vertex t, vertex t1 where t.idx <> t1.idx and t1.idx = t.idx + 1 );
La ligne de départ
[img]http://i59.tinypic.com/11snkeg.png[/img]
table des routes bd_topo suivant la ligne:
le UNION permet d'ajouter les troncons de départ et d'arrivée a cette table, qui contient pour chaque segment
de ligne le segment bd route qui lui correspond
Code:
drop table route_tmp; create table route_tmp as ( SELECT r.gid AS route_gid, r.geom FROM route r, ligne l WHERE st_contains(st_buffer(l.geom, 1), r.geom) UNION -- route de depart select r.gid, r.geom FROM route r, vertex v where v.idx = 1 and st_dwithin(v.geom, r.geom, 0.01) UNION --route d'arrivée select r.gid, r.geom FROM route r, vertex v where v.idx = (select max(idx) from vertex) and st_dwithin(v.geom, r.geom, 0.01) );
Routes contenant chaque segment: on crée un identifiant croissant avec row_number, différent de s.idx
qui peut ne pas etre continu si on ne retrouve pas un segment de bd topo:
Cet id servira dans la fonction récursive pour avancer dans le sens de la ligne de départ
Code:
drop table if exists seg_route; create table seg_route as ( SELECT s.idx, r.route_gid, row_number() OVER (ORDER BY s.idx) as id, r.geom FROM segments s, route_tmp r WHERE st_dwithin(r.geom, st_geometryN(s.geom, 1), 1) AND st_dwithin(r.geom, st_geometryN(s.geom, 2), 1) );
Enfin, la fonction récursive: pour chaque tronçon consécutifs, on repère quand on a affaire à un nouveau troncon et on note
son identifiant, ou null si l'identifiant est le meme que le précédent: on est tjs sur le meme troncon de bd route.
A la fin de la recursion, on ne garde que les routes ayant un identifiant non null et on regénère un id croissant, qui sera le numero de segment
ordonné par rapport à la ligne initiale
Code:
with recursive tmp as ( select idx, id, route_gid, route_gid as newid from seg_route where id = 1 UNION select s.idx, s.id, s.route_gid, case when t.route_gid <> s.route_gid then s.route_gid else null end as newid from seg_route s, tmp t where s.id = t.id + 1 ) select row_number() over (ORDER BY id) as path, r.geom from tmp t join route_tmp r on t.newid = r.route_gid where newid is not null;
On obtient une série de segments de bd route ordonnés dans le sens de parcours de la ligne:
En noir la ligne initiale, en bleu les segments BD route retrouvés avec leur ordre
[img]http://i60.tinypic.com/2zhlefr.png[/img]
Les boucles sont gérées: les troncons à plusieurs passages sont dupliqués dans le résultat final:
[img]http://i60.tinypic.com/2nsrvv4.png[/img]
Nico
Hors ligne
#18 Fri 24 October 2014 11:36
- Nicolas Ribot
- Membre
- Lieu: Toulouse
- Date d'inscription: 9 Sep 2005
- Messages: 1554
Re: [PostGis] Caler une LineString sur des tronçons d'un référentiel
Sinon, dans quels cas vous obtenez des trous dans le réseau BD Route ?
Ce sont des troncons bd route manquants ou non retrouvés par rapport a la ligne initiale ?
nico
Hors ligne
#19 Fri 24 October 2014 14:36
- jerome_m
- Participant actif
- Lieu: Montpellier
- Date d'inscription: 28 Nov 2012
- Messages: 51
Re: [PostGis] Caler une LineString sur des tronçons d'un référentiel
Enorme merci Nicolas pour ce travail ! Je pense qu'il va servir à de nombreuses personnes vu le peu d'info disponibles sur internet par rapport à cette problématique !
Je reprendrais tout cela la semaine prochaine pour bien l'annalyser et l'appliquer à toutes nos lignes, mais dans tous les cas, j'ai énormément appris avec ces discussions sur les LineString et cela va nous permettre de créer et d'organiser nos données d'une façon efficace.
Pour les "trous" observés dans la BD route, il n'y en a pas dans cet échantillon (volontaire). Cela se produit dans le cas de troncons manquants, (principalement des nouvelles routes par rapport au référentiel utilisé). C'est pour cela qu'il n'est pas nécessaire d'augmenter le buffer de recherche, cela ne servirait à rien.
Je vais essayer de tester rapidement comment se comporte cette méthode lorsqu'il y a des tronçons manquants, et trouver une solution pour répondre entièrement au besoin.
Merci aussi Pascal pour les informations sur le style d'affichage des LineString !
Dernière modification par jerome_m (Fri 24 October 2014 14:37)
Hors ligne
Pages: 1
- Sujet précédent - [PostGis] Caler une LineString sur des tronçons d'un référentiel - Sujet suivant