Nous utilisons des cookies pour vous garantir la meilleure expérience sur notre site. Si vous continuez à utiliser ce dernier, nous considèrerons que vous acceptez l'utilisation des cookies. J'ai compris ! ou En savoir plus !.
banniere

Le portail francophone de la géomatique


Toujours pas inscrit ? Mot de passe oublié ?
Nom d'utilisateur    Mot de passe              Toujours pas inscrit ?   Mot de passe oublié ?

Annonce

Printemps des cartes 2024

#1 Mon 15 February 2016 09:49

CGuillaume
Participant actif
Lieu: Annecy
Date d'inscription: 3 Aug 2011
Messages: 59

[PostGIS] Découpages fonds de vallées

Bonjour,

Je cherche, avec PostGIS si possible, à découper des fonds de vallées pour les affecter à des tronçons de cours d’eau. Un petit exemple en image en pj : pour le tronçon vert lui affecter la zone colorisée en vert avec un découpage correspondant (grossièrement) aux lignes jaunes.

J’avais pensé faire de larges buffers autour des lignes, avec les extrémités plates, pour ensuite découper les fonds de vallées par les buffers.

Code:

 st_buffer(a.geom, 100, 'endcap=flat join=bevel')

d'après la doc smile

Sauf que ça ne me donne pas toujours des extrémités plates (pourquoi ?) et que j’ai beaucoup de superpositions…(ce qui n’est pas le plus grave en soit).

Avez-vous une autre idée pour automatiser ce genre de découpage ?

Merci d’avance pour vos propositions qui seront j’en suis persuadé très élégantes !

Guillaume


Fichier(s) joint(s) :
Pour accéder aux fichiers vous devez vous inscrire.

Hors ligne

 

#2 Mon 15 February 2016 16:41

Nicolas Ribot
Membre
Lieu: Toulouse
Date d'inscription: 9 Sep 2005
Messages: 1536

Re: [PostGIS] Découpages fonds de vallées

Bonjour,

Si vous disposez de chaque troncon de rivière sous forme de linestring (avec points de départ et d'arrivée), vous pouvez utilisez cette fonction très pratique:

https://trac.osgeo.org/postgis/wiki/Use … WithOffset

pour chaque start/end de chaque troncon, tracer les perpendiculaires au troncon puis découper les fonds de vallée.

Avec un buffer, ca peut aussi marcher, mais si le cours d'eau est tortueux par rapport au fond, les résultats peuvent etre bizarres

Nico

Hors ligne

 

#3 Tue 16 February 2016 13:06

Nicolas Ribot
Membre
Lieu: Toulouse
Date d'inscription: 9 Sep 2005
Messages: 1536

Re: [PostGIS] Découpages fonds de vallées

Bonjour,

Le problème n'est simple, notamment pour fabriquer les lignes jaunes aux intersections des tronçons de cours d'eau:
si le cours est tortueux, et si on prend comme ligne jaune la normale au tronçon, alors il se peut que les lignes se recoupent DANS le fond, créant un polygone en papillon. (le même type de problème doit se poser en prenant un buffer aux extrémités carrées pour chaque tronçon)

J'ai testé un peu hier soir en prenant les normales aux tronçons pour les points d'intersection des tronçons (fn st_lineInterpolatePoint du wiki postgis)
A partir de ces lignes, on peut délimiter des parties intersectant le fond de vallée et reconstruire des polygones (st_polygonize) à partir de ces lignes et du polygone de fond.
On peut alors associer chaque polygone au tronçon de rivière concerné.

Le pb est alors d'identifier les parties de polygones en papillon, formés par les lignes jaunes qui se croisent dans le pg de fond.

En SQL, étape par étape, ca donne ca (on s'accroche aux rideaux big_smile ) :

• pour calculer les lignes jaunes, on prend la normale moyenne en un point: en effet, en un point, deux tronçons se touchent. si les tronçons ont des angles différents, deux normales peuvent etre calculées en ce point, une pour chaque tronçon.
Là, il y a matière à amélioration en faisant en sorte, après ce calcul, que les lignes jaunes ne s'intersectent pas à l'intérieur du fond

Code:

with tmp as (
  -- on calcule les normales de chaque coté de chaque tronçon (distance de 300m prise au pif en fonction des données de test)
  SELECT
    c.dbid,
    st_startpoint(geom) AS geom,
    st_lineinterpolatepoint(geom, 0.01, 300) as pt_left,
    st_lineinterpolatepoint(geom, 0.01, -300) as pt_right
  FROM testcours c
  UNION ALL
  SELECT
    c.dbid,
    st_endpoint(geom) AS geom,
    st_lineinterpolatepoint(geom, 1, 300) as pt_left,
    st_lineinterpolatepoint(geom, 1, -300) as pt_right
  FROM testcours c
), tmp1 as (
    -- on regroupe les points identiques a partir desquels on va calculer la normale aux lignes en ce point
    -- pour certains points, on a deux lignes (lignes connectées), pour d'autres, une seule (extrémité de ligne)
    SELECT
      array_agg(t.dbid) AS dbids,
      array_agg(t.pt_left) AS lefts,
      array_agg(t.pt_right) AS rights,
      t.geom
    FROM tmp t
    GROUP BY t.geom
), tmp2 as (
  -- on duplique la ligne si une seule ligne en ce point, 
  --histoire de pouvoir tout le temps calculer une normale moyenne entre deux lignes (même identiques)
  select t.geom,
    case when array_length(t.dbids, 1) = 1 then dbids||dbids[1] else dbids end as dbids,
    case when array_length(t.lefts, 1) = 1 then lefts||lefts[1] else lefts end as lefts,
    case when array_length(t.rights, 1) = 1 then rights||rights[1] else rights end as rights
  from tmp1 t
), tmp3 as (
  -- on calcule les perpendiculaires moyennes en chaque point, qui donnent les lignes jaunes
  select t.dbids, st_makeLine(
        st_lineinterpolatepoint(st_makeLine(lefts[1], lefts[2]), 0.5),
        st_lineinterpolatepoint(st_makeLine(rights[1], rights[2]), 0.5)) as geom
  UNION ALL
  -- on ajoute l'exterieur du pg pour avoir un reseau de lignes (lignes jaunes + exterieur du PG) dont on veut reconstruire les surfaces
    select null::bigint[] as dbids, st_boundary(geom) as geom
    from testpg
  from tmp2 t
) , tmp4 as (
  -- on fabrique l'union des lignes et de l'exterieur du pg, avec st_union, histoire d'avoir un réseau de lignes connectées aux intersections,
  -- condition indispensable pour que st_polygonize marche
  select (st_dump(st_union(t.geom))).geom
  from tmp3 t
), tmp5 as (
  -- on construit tous les polygones issus des croisements des lignes
  -- précédentes avec st_polygonize, et on dump la collection obtenue en polygones individuels
  select (st_dump(st_polygonize(t.geom))).geom as geom
  from tmp4 t
), tmp6 as (
  -- on conserve les polygones contenus dans le fond:
  -- --pb de precision ici: st_contains ne renvoie pas les bons résultats: petit buffer négatif pour contourner le pb de précision
  select t.geom
  from tmp5 t, testpg p
  where st_contains(p.geom, st_buffer(t.geom, -0.1)) 
), tmp7 as (
  -- on associe les pg de fond issus du découpage aux tronçons, en prenant le milieu des troncons
  select c.dbid, t.geom
  from testcours c join tmp6 t
      on st_contains(t.geom, st_lineinterpolatepoint(c.geom, 0.5))
) select * from tmp7;

En image: le pg de fond est en bleu clair: on voit les pg en papillon pas identifiés dans la requete.
Une passe supplémentaire en cherchant les polygones qui touchent les lignes jaunes devrait permettre d'identifier ces pg.

Nicolas


Fichier(s) joint(s) :
Pour accéder aux fichiers vous devez vous inscrire.

Hors ligne

 

#4 Tue 16 February 2016 14:46

CGuillaume
Participant actif
Lieu: Annecy
Date d'inscription: 3 Aug 2011
Messages: 59

Re: [PostGIS] Découpages fonds de vallées

Bonjour Nicolas,

Bon j'en étais que là moi... et je cherchais à faire des polygones et les réaffecter à mes tronçons sad

Code:

-- Création perpendiculaire en fin de tronçon
SELECT gid,
        ST_MakeLine(ARRAY[st_line_interpolate_point(geom,0.001,-500), st_line_interpolate_point(geom,1,500)]) as geom
from troncons;

-- Création perpendiculaire en début de tronçon
SELECT gid,
        ST_MakeLine(ARRAY[st_line_interpolate_point(geom,0.001,-500), st_line_interpolate_point(geom,1,500)]) as geom
from troncons;

J'ai testé avec mon échantillon de données ça tourne bien big_smile

Maintenant il ne me reste plus qu'à reprendre ta requête étape par étape pour l'assimiler !
Je pense qu'en trouvant la bonne valeur de distance pour la construction des normales on peut limiter les papillons non identifiés et les récupérer par la suite.

En tout cas, merci beaucoup pour le boulot et respect ! J'adore ce forum.

@+
Guillaume

Hors ligne

 

#5 Tue 16 February 2016 18:09

Nicolas Ribot
Membre
Lieu: Toulouse
Date d'inscription: 9 Sep 2005
Messages: 1536

Re: [PostGIS] Découpages fonds de vallées

Bonjour,

Merci. Oui, c'est un bon forum wink

Une autre idée m'est venue, peut etre plus simple:

• On découpe le polygone de fond en 2 berges, grâce au cours d'eau berge 1 et berge 2
• on travaille par tronçon de cours d'eau:
   - pour chaque tronçon, on calcule les points sur chaque berge les plus proches du startpoint et endpoint du troncon: on obtient 4 points par tronçon:
berge 1, proche du start: sb1
berge 1, proche du end: eb1
berge 2, proche du start: sb2
berge 2, proche du end: eb2

On peut alors reconstruire 4 linestrings:
  - une composée de sb1, startpoint, sb2
  - une composée de eb1, endpoint, eb2
  - une composée par la sub linestring de la berge 1 découpée entre les points sb1 et eb1
  - une composée par la sub linestring de la berge 2 découpée entre les points sb2 et eb2

On peut alors merger ces 4 lignes, qui doivent (à la précision près du découpage), reconstruire des polygones délimitant les zones de tronçons.
La méthode des points les plus proches à l'air de mieux marcher que les normales aux fins de segments.

Dans certains cas, on peut avoir un mauvais ordre des points de découpage des berges, mais il est possible de les réordonner correctement dans une sous-requête.

Les sous-requetes jouent avec les tableaux postgres: c'est super puissant pour construire des géométries à partir de morceaux de geom.
Les tableaux peuvent etre mis en lignes facilement (unnest(array)).

En SQL:

Code:

-- découpage du pg en deux berges:
drop table berge;
create table berge as (
    with tmp as (
    select st_union(geom) as geom
    from testcours
  ), tmp1 as (
    SELECT st_dump(st_difference(st_exteriorring(p.geom), t.geom)) as d
    FROM testpg p, tmp t
  ) select (t.d).path[1], (t.d).geom
  from tmp1 t
);

-- puis exploitation des berges et des troncons:

with tmp as (
  -- on calcule les points les plus proches des start et end points sur chaque berge (st_snapToGrid pour eviter des pb de précision)
  select c.dbid,
    st_startpoint(c.geom) as start,
    st_snapToGrid(st_lineinterpolatepoint(b1.geom, st_linelocatepoint(b1.geom, st_startpoint(c.geom))), 0.01) AS sb1,
    st_snapToGrid(st_lineinterpolatepoint(b1.geom, st_linelocatepoint(b1.geom, st_endpoint(c.geom))), 0.01) AS eb1,
    st_endpoint(c.geom) as end,
    st_snapToGrid(st_lineinterpolatepoint(b2.geom, st_linelocatepoint(b2.geom, st_endpoint(c.geom))), 0.01) AS eb2,
    st_snapToGrid(st_lineinterpolatepoint(b2.geom, st_linelocatepoint(b2.geom, st_startpoint(c.geom))), 0.01) AS sb2,
    -- on garde aussi la position du point d'intersection sur chaque berge, elle servira a st_lineSubString()
    st_linelocatepoint(b1.geom, st_startpoint(c.geom)) AS lsb1,
    st_linelocatepoint(b1.geom, st_endpoint(c.geom)) AS leb1,
    st_linelocatepoint(b2.geom, st_endpoint(c.geom)) AS leb2,
    st_linelocatepoint(b2.geom, st_startpoint(c.geom)) AS lsb2,
    b1.geom as b1geom,
    b2.geom as b2geom

  from testcours c, berge b1, berge b2
  WHERE b1.path = 1 AND b2.path = 2

), tmp1 as (
  -- on construit 4 arcs de ligne pour chaque troncon: ces arcs sont constitués
  -- des points calculés précédemment et des partie de berge interctées par les points sb1/eb1 et eb2/sb2
  select t.dbid,
    -- arc 1: le segment dans le fond de vallee, au niveau du startpoint
    st_snaptogrid(st_makeLine(array[t.sb2, t.start, t.sb1]), 0.01) as sub1,
    -- arc2: partie de berge 1
    st_snaptogrid(st_lineSubstring(b1geom, least(t.lsb1, t.leb1), greatest(t.lsb1, t.leb1)), 0.01) as sub2,
    -- arc 3: le segment dans le fond de vallee, au niveau du endpoint
    st_snaptogrid(st_makeLine(array[t.eb1, t.end, t.eb2]), 0.01) as sub3,
    -- arc4: partie de berge 2
    st_snaptogrid(st_lineSubstring(b2geom, least(t.lsb2, t.leb2), greatest(t.lsb2, t.leb2)), 0.01) as sub4
  from tmp t

), tmp2 as (
    -- on remet le tableau de géométries en ligne, car st_lineMerge prend une multilinestring à fusionner.
    -- pour chaque troncon, on obtient 4 lignes qu'on pourra GROUP BY dbid plus tard
    SELECT
      t.dbid,
      unnest(ARRAY [sub1, sub2, sub3, sub4]) AS lines
    FROM tmp1 t

), tmp3 as (
    -- on tente de reconstruire les polygones en mergeant les lignes par id de troncon
    SELECT
      t.dbid,
      st_lineMerge(st_union(t.lines)) as lines
    FROM tmp2 t
    GROUP BY t.dbid

)
-- on filtre les éventuels pg en erreur: on n'arrive pas a construire un pg a partir des 4 lignes
-- TODO: réordonner les points d'intersection sur les berges pour découper correctement 
select dbid, st_makePolygon(lines)
from tmp3
where geometryType(lines) = 'LINESTRING';

En image: (il manque juste un tout petit bout non reconstruit, en haut à droite)

Nicolas


Fichier(s) joint(s) :
Pour accéder aux fichiers vous devez vous inscrire.

Hors ligne

 

#6 Fri 19 February 2016 09:00

CGuillaume
Participant actif
Lieu: Annecy
Date d'inscription: 3 Aug 2011
Messages: 59

Re: [PostGIS] Découpages fonds de vallées

Bonjour,

Effectivement même si j'ai mis un peu de temps à reproduire le trick chez moi, ça marche encore mieux !
J'ai galéré avec mes tronçons de test qui ont pas mal d'affluents qui coupent le fond de vallée, du coup je me retrouver avec plus de 2 berges lors de la découpe ce qui est un peu moins logique smile

Mais après un petit travail pour sélectionner les tronçons "principaux" ça devrai le faire.

Merci Nicolas.
@Bientôt
Guillaume

Hors ligne

 

Pied de page des forums

Powered by FluxBB