#1 Wed 24 April 2013 17:25
- ppluvinet
- Participant assidu
- Lieu: VALENCE
- Date d'inscription: 6 Aug 2007
- Messages: 617
POSTGIS : couper des lignes par des points
Bonjour,
J'ai des polylignes que je veux segmenter par une couche de points. Ces points sont préalablement snappés aux lignes.
J'ai testé deux solutions dont aucune est concluante :
Code:
--solution 1 avec st_difference drop table lines_dec; create table lines_dec as select a.idline, (ST_Dump(ST_difference(a.geom,b.geom))).geom as geom from lines a , points_multi b where st_intersects(a.geom,b.geom) and a.idline= b.idline; --solution 2 avec st_split drop table lines_dec; create table lines_dec as select a.idline, (ST_Dump(ST_Split(a.geom,b.geom))).geom as geom from lines a , points_non_multi b where st_intersects(a.geom,b.geom);
Pour la solution 1, j'ai préalablement fusionné (avec un st_union) les points qui intersectent la même ligne. Le résultat (lines_dec) n'est rien d'autre que les segments de ma polyline qui intersectent mes points. Les segments n'ont pas été sectionné par les points.
Pour la solution 2, les segments sont bien sectionnés par les points. Or, dans le cas où j'ai plusieurs points sur une même ligne, comme la fonction st_split n'accepte pas les Multipoint, j'ai des segments qui se chevauchent (sorte de doublons)....
Auriez-vous une autre solution ? Merci d'avance,
Pascal
Pascal PLUVINET
Hors ligne
#2 Thu 25 April 2013 08:27
- Nicolas Granier
- Participant assidu
- Date d'inscription: 19 Apr 2007
- Messages: 271
Re: POSTGIS : couper des lignes par des points
Bonjour,
J'ai mis au moins 2 jours à faire ma requête de découpage alors si je peux vous en faire bénéficier c'est avec plaisir.
Tout d'abord voici les conclusions que j'ai pu tirer de mes tests :
-un point n'est jamais positionné exactement sur un tronçon de ligne, tout n'est qu'une question de tolérance.
- il faut effectivement regrouper les points en une couche sinon ca duplique les résultats en sortie.
voici la requete :
Code:
select couche_de_ligne.champ1,couche_de_ligne.champ2, ST_SNAP((ST_DUMP(st_difference(couche_de_ligne.the_geom,point))).geom,all_point,0.1) as the_geom from (select ST_Multi(ST_Union(st_expand(couche_de_point.the_geom, 0.05))) as point from couche_de_point ) as t1, couche_de_ligne,(select ST_MULTI(ST_COLLECT(couche_de_point.the_geom))as all_point from couche_de_point) as t2
et voici l'explication :
Afin de palier au problème de tolérance, je convertis mes points en buffer (très petit 5cm). J'ai préalablement assemblé ces points en une seule couche de multipoint (avec ST_UNION). Je découpe avec ST_Différence puis ST_Dump comme dans votre solution 1, et je rajoute un ST_SNAP à 10cm, pour recoller les extrémités de mes lignes découpées sur les points de découpage eux-même, mais que j'ai assemblé avec ST_COLLECT ET non ST_UNION afin de leur conserver des géométries différenciées.
Nicolas GRANIER
Hors ligne
#3 Thu 25 April 2013 09:42
- ppluvinet
- Participant assidu
- Lieu: VALENCE
- Date d'inscription: 6 Aug 2007
- Messages: 617
Re: POSTGIS : couper des lignes par des points
Et bien bravo ! Ca fonctionne ! c'est efficace et relativement élégant à la fois ! Plusieurs sujets (notamment anglophones) traitent de ce sujet mais aucun n'envisage une solution si efficace (du moins ceux que j'ai pu lire). Merci Nicolas.
A bientôt,
Pascal PLUVINET
Hors ligne
#4 Thu 25 April 2013 09:52
- Nicolas Granier
- Participant assidu
- Date d'inscription: 19 Apr 2007
- Messages: 271
Re: POSTGIS : couper des lignes par des points
Mais de rien. J'ai fait moi même pas mal de veille sans trouver un truc qui fonctionne à 100%, alors j'ai assemblé plusieurs morceaux de sql pour parvenir à cette solution.
A bientôt.
Nicolas GRANIER
Hors ligne
#5 Sat 27 April 2013 18:46
- Nicolas Ribot
- Membre
- Lieu: Toulouse
- Date d'inscription: 9 Sep 2005
- Messages: 1554
Re: POSTGIS : couper des lignes par des points
Bonjour Nicolas,
La methode est interessante !
Cependant, en regroupant tous les points dans une seule couche, les index spatiaux ne peuvent plus etre utilisés lors de la requete.
Pour gagner en performance, il est possible de regrouper les points par ligne dans une collection, puis de faire la difference entre une ligne et ce groupe de points.
Le snap est ensuite fait entre les multilinestring et leurs multipoints respectifs.
La premiere requete fabrique les groupes de multipoints par ligne, en gardant l'id de la ligne pour faire la jointure dans la requete d'apres.
Elle fabrique aussi une collection des points initiaux, pour permettre le snapping entre les lignes et leurs points:
Code:
with mpoint_by_line as ( select l.gid as lgid, (st_collect(st_expand(p.geom, 0.01))) as cut, st_collect(p.geom) as geom from points p, lines l where st_dwithin(l.geom, p.geom, 0.01) group by l.gid ) select l.gid, (st_dump(st_snap(st_difference(l.geom, mp.cut), mp.geom, 0.1))).geom as geom from mpoint_by_line mp, lines l where mp.lgid = l.gid;
La difference de perf peut etre interessante sur les gros volumes de données:
~ 290 ms contre 24500 ms avec le meme jeu de données.
Nicolas
Hors ligne
#6 Sat 27 April 2013 20:14
- Nicolas Ribot
- Membre
- Lieu: Toulouse
- Date d'inscription: 9 Sep 2005
- Messages: 1554
Re: POSTGIS : couper des lignes par des points
Re
Je me suis demandé s'il etait possible de realiser la meme chose avec les fonctions de referencement linéaire, qui permettent de couper des lignes et de projeter des points sur des lignes proches, realisant un snapping tres precis et tres rapide.
Le principe est de trouver pour chaque ligne, la position de chaque point de decoupage par rapport a la ligne: 0 represente le debut de la ligne, 1 la fin de la ligne. Il s'agit de la localisation du point sur la ligne.
Puis, pour pouvoir realiser le decoupage des lignes initiales en se servant des differentes localisations des points sur la ligne, une table est construite en creant un champ index, incremental ( partie "rank() over (partition...)" ) . Cet index va permettre de fabriquer un record avec les champs: geom, localisation, localisation + 1
Enfin, les lignes initiales sont découpés par les localisations des points (st_line_substring), en realisant les jointures qui vont bien:
Code:
-- premiere table des positions des points par rapport a "leurs" lignes with locus as ( -- on genere la position du debut de la ligne (index 0) pour le premier segment à découper select l.gid, 0 as l from lines l UNION ALL -- on genere la position de la fin de la ligne (index 1) pour le dernier segment à découper select l.gid, 1 as l from lines l UNION ALL -- calcul des positions des points par rapport aux lignes select l.gid, st_line_locate_point(l.geom, p.geom) as l from lines l, points p where st_dwithin(l.geom, p.geom, 0.01) order by gid, l ), -- deuxieme table qui ajoute un index croissant par position croissante, pour permettre de faire la jointure plus tard sur elle-meme: loc_with_idx as ( select gid, l, rank() over (partition by gid order by l) as idx from locus ) -- decoupage final de chaque ligne avec les positions des points sur la ligne, grace a st_line_substring select l.gid, st_line_substring(l.geom, loc1.l, loc2.l) as geom from loc_with_idx loc1, loc_with_idx loc2, lines l where l.gid = loc1.gid and loc1.gid = loc2.gid and loc2.idx = loc1.idx+1;
L'avantage de cette methode est que le snapping entre les points et les lignes est super rapide et precis, quelque soit la precision.
En terme de vitesse, c'est tres performant ! : 90ms avec le meme jeu de donnée que dans le post précédent.
Hors ligne
#7 Mon 29 April 2013 08:18
- Nicolas Granier
- Participant assidu
- Date d'inscription: 19 Apr 2007
- Messages: 271
Re: POSTGIS : couper des lignes par des points
Bonjour Nicolas,
Effectivement je me trouve devant des temps de calculs assez importants (enfin c'est relatif), je parle de 6H de traitement sur ma machine pour environ 10 000 tronçons à découper par 50 000 points. J'avais juger que mes 6 h de traitements étaient acceptables ... vue que je n'avais pas d'autre solution
Pour mon prochain gros calcul, j'essaye le "with" avec l'utilisation des index spatiaux.
Nicolas GRANIER
Hors ligne
#8 Mon 29 April 2013 09:55
- Nicolas Ribot
- Membre
- Lieu: Toulouse
- Date d'inscription: 9 Sep 2005
- Messages: 1554
Re: POSTGIS : couper des lignes par des points
Bonjour,
Oui 6h de traitement pour cette volumetrie, c'est un peu trop.
la methode la plus rapide (referencement lineaire) prend environ 20 s pour une volumetrie de 109133 lignes des communes croisées avec 800 000 points sur ces lignes.
La methode WITHIN prend environ 120% de plus.
Nicolas
Hors ligne