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

Rencontres QGIS 2025

L'appel à participation est ouvert jusqu'au 19 janvier 2025!

#1 Fri 17 April 2020 11:52

preliator
Participant assidu
Date d'inscription: 17 Nov 2018
Messages: 433

Couper une ligne tous les X mètres

Bonjour,

Disposant d'une couche multilinestring représentant les routes d'un département, je souhaiterais couper cette couche en lignes de 500m.
Voici un aperçu de ma couche de routes : https://zupimages.net/viewer.php?id=20/16/eh9e.png

Après quelques recherche sur internet, je suis parti sur cette démarche :

- Regrouper ma couche de route en un seul bloc (j'ai trouvé cela cohérent car la couche de base est constitués de plusieurs lignes de tailles variables)

- Transformer ce bloc en linestring pour la requête suivante

- Créer des points tous les 500m le long de ma linestring avec st_lineinterpolatepoints (pas possible de créer les points sur ma couche regroupée, car cette fonction n'accepte que du linestring)

- Couper la couche regroupée sur cette couche de points.

Code:

-- REGROUPER MA COUCHE DE ROUTE EN UN SEUL BLOC MULTILINESTRING
drop table if exists ligne_regroup;
create table ligne_regroup as
select St_linemerge(ST_Union(geom)) as geom
from public.route_echantillon;

-- TRANSFORMER LE BLOC EN LINESTRING
drop table if exists ligne_regroup_linestring;
CREATE TABLE ligne_regroup_linestring AS
   SELECT (ST_Dump(ligne_regroup.geom)).geom::Geometry(LineString,2154) AS geom
   FROM ligne_regroup;

-- CREER UN IDENTIFIANT UNIQUE SUR LA LINESTRING
alter table ligne_regroup_linestring add column id serial;

-- CREER LES POINT TOUS LES 500 METRES
drop table if exists point_500;
create table point_500 as
with tmp as(
    select *
    from ligne_regroup_linestring
    where st_length(ligne_regroup_linestring.geom) > 500)
SELECT id, st_lineinterpolatepoints((tmp.geom),500/st_length(tmp.geom)) as geom
from tmp;

-- ON COUPE LA ROUTE REGROUPEE SUR LES POINTS
drop table if exists route_cut_500_metres;
create table route_cut_500_metres as
select ST_SNAP((ST_DUMP(st_difference(ligne_regroup.geom,point))).geom,all_point,0.1) as geom 
from 
(select ST_Multi(ST_Union(st_expand(point_500.geom, 0.05))) as point from point_500 ) as t1, 
ligne_regroup,(select ST_MULTI(ST_COLLECT(point_500.geom))as all_point from point_500) as t2;

Malheureusement, j'obtiens des résultats aberrants. Les lignes semblent bien se découper, mais il manque de nombreuses lignes.

Comme on peut voir sur l'exemple suivant : https://zupimages.net/viewer.php?id=20/16/oaem.png

En vert : ma couche de route regroupée (ligne_regroup)
En rouge : le résultat de "route_cut_500_metres"
Les points : Résultat de la requête qui me crée des points tous les 500 mètres.

Merci.

Dernière modification par preliator (Fri 17 April 2020 11:59)

Hors ligne

 

#2 Fri 17 April 2020 13:56

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

Re: Couper une ligne tous les X mètres

Bonjour,

Il y a pas mal de posts sur georezo avec des exemples pour découper des lignes régulèrement.
Pas besoin de regrouper ou générer des points tous les 500m: vous pouvez dumper les lignes et pour chacune, appeler st_lineSubstring avec la valeur qui va bien pour la valeur endfraction, qui devra s'incrémenter pour chaque tronçon de 500m découpable dans les lignes.

Nico

Hors ligne

 

#3 Fri 17 April 2020 15:00

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

Re: Couper une ligne tous les X mètres

Voici un moyen de faire:

• dumper les multilinestrings en LN simples, en gardant l'index de la ligne dans la collection (st_dump(geom).path[1]): ca servira a reconstruire les multilinestrings plus tard.

• calculer le nombre de découpage a faire pour chaque ligne: longueur_ligne/longueur_découpe (ici, 50m, sur mon dataset, 500m ne découpait rien): numcut; et calculer combien représentent 50m en fraction de la ligne a découper (chiffre entre 0 et 1, a passer a st_linesubstring (https://postgis.net/docs/manual-2.5/ST_ … tring.html) )

• générer (avec generate_series) le bon nombre de découpage pour chaque ligne: on doit découper entre 0 et numcut portions. Le cross join lateral permet d'appeler generate_series avec une valeur de numcut différente pour chaque ligne.

• calculer les valeurs de startfraction et endfraction pour chaque découpage (chiffre entre 0 et 1, a passer a st_linesubstring)

• Enfin, découper la ligne avec le startfraction et endfraction calculés. (il reste éventuellement a regrouper les lignes de meme id, order by idln, pour reconstruire les multilinestrings initiales, mais avec des segments découpés a 50m)

En sql:

Code:

with tmp as (
    select id, (st_dump(geom)).path[1] as idln, (st_dump(geom)).geom
    from work_nico.roadml
), tmp1 as (
    select id, idln, floor(st_length(geom) / 50.0)::int as numcut,
           (50/st_length(geom)) as frac_50m,
           geom
    from tmp
), tmp2 as (
    select t.*, g.cutpos, g.cutpos * frac_50m as startfraction,
           least((g.cutpos * frac_50m) + frac_50m, 1) as endfraction
    from tmp1 t
             cross join lateral (
        select cutpos
        from generate_series(0, t.numcut) as cutpos
        ) g
) select t.id, idln, numcut, frac_50m, startfraction, endfraction,
         st_linesubstring(geom, startfraction, endfraction) as geom
from tmp2 t;

Ici, je me suis arrêté au découpage des routes, je n'ai pas regroupé en MLN (st_collect(geom order by idln))

En image: en vert, les routes initiales avec startpoint/endpoint sous forme de flêches.
En rouge, les startpoint/endpoint des lignes découpées tous les 50m

Nico


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

Hors ligne

 

#4 Fri 17 April 2020 15:36

preliator
Participant assidu
Date d'inscription: 17 Nov 2018
Messages: 433

Re: Couper une ligne tous les X mètres

C'est ... impressionnant ! Ca fonctionne très bien.

Je vous remercie sincèrement pour votre temps smile

Hors ligne

 

#5 Fri 17 September 2021 11:58

Mathieu Denat
Participant actif
Lieu: Montpellier
Date d'inscription: 5 May 2010
Messages: 110

Re: Couper une ligne tous les X mètres

Bonjour,

Tout d'abord merci Nico et Preliator pour cet échange qui m'a servi un très grand nombre de fois! Notamment pour découper des cours d'eau par tronçons de 500m. L'idée est simple: découper un cours d'eau de l'aval vers l'amont selon des tronçons de 500 m. Ensuite, je suis amené à compter les entités ou des linéaires ou des surfaces d'espèces végétales par tronçon.

En général ce script fonctionne sans que je me pose trop de question, mais cette fois ci c'est l'impasse.
Mon cours d'eau en entrée, fait ~ 25 km de long, je devrais donc obtenir 51 tronçons (50 x 500m + 1 x moins de 500m à l'amont)
Mon problème est que le script ne génère pas de tronçon de 500 m, et je ne comprend pas pourquoi...

À l'origine mon cours d'eau est composé de 79 portions (il est issu de la BDTopage).
À la sortie j'ai toujours mes 79 tronçons, dont la longueur varie de 6 à 650m environ.

J'uploade en PJ mes données (le résultat du 1er select (boyne), histoire de pouvoir tester grandeur nature!), et voici mon script SQL (j'ai ajouté la longueur des tronçons pour vérifier ce qui clochait).
Avez-vous une idée de ce qui ne va pas?
Merci d'avance! smile

Code:

WITH

boyne AS (
    SELECT
        cdcourseau,
        geom
    from referentiel.cours_eau where topooh ilike '%boyne' -- sélection de la Boyne
    )

,tmp AS (
    SELECT cdcourseau, (st_dump(geom)).path[1] as idln, (st_dump(geom)).geom
    from boyne )
--    from outils.cours_eau_simplifie ) --utilisé uniquement dans le cas de cours d'eau contenant des plans d'eau

,tmp1 AS (
    SELECT
        cdcourseau,
        idln,
        floor(st_length(geom) / 500.0)::int as numcut,
        (500/st_length(geom)) as frac_500m,
        geom
    FROM tmp)

,tmp2 as (
    SELECT
        t.*,
        g.cutpos,
        g.cutpos * frac_500m as startfraction,
        least((g.cutpos * frac_500m) + frac_500m , 1) as endfraction
    FROM tmp1 t
    CROSS JOIN LATERAL (
        SELECT cutpos
        FROM generate_series(0, t.numcut) as cutpos
        ) g )

SELECT st_length(geom) longueur,
        t.cdcourseau,
        idln, numcut, frac_500m, startfraction, endfraction,
        st_linesubstring(geom, startfraction, endfraction) as geom,
         concat(cdcourseau,'-',to_char(row_number() over (),'FM000')) id_troncon
FROM tmp2 t
order by 1 --id_troncon

Dernière modification par Mathieu Denat (Fri 17 September 2021 12:01)


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

Mathieu
C'est en forgeant qu'on devient forgeron

Hors ligne

 

#6 Fri 17 September 2021 20:36

tumasgiu
Membre
Lieu: Ajaccio
Date d'inscription: 5 Jul 2010
Messages: 1160

Re: Couper une ligne tous les X mètres

Salut,

votre requête est bonne, c'est votre dataset qui "cloche" : votre geometrie est multilinestring composé  pour la plupart de segments de - de 500m.
En recréant une seule lneestring à partir de votre geometrie grâce à st_linemerge dans votre premiere CTE,  vous arrivez au résultat attendu.

Dernière modification par tumasgiu (Fri 17 September 2021 20:51)

Hors ligne

 

#7 Sat 18 September 2021 20:44

Mathieu Denat
Participant actif
Lieu: Montpellier
Date d'inscription: 5 May 2010
Messages: 110

Re: Couper une ligne tous les X mètres

Bonsoir tumasgiu,
Merci pour votre réponse.
Ça ne fonctionne pas du premier coup, je ne comprends pas pourquoi, mais vous m'avez débloqué! smile
Mais en creusant dans ce sens, j'ai trouvé une alternative: faire un st_memunion suivi d'un st_linemerge et hop ça fonctionne.
D'un point de vue géométrique, je ne comprends pas pourquoi le st_linemerge ne fonctionne pas, si l'un⋅e d'entre vous à une idée, je suis preneur.
À bientôt.

Voici le script définitif, si ça peut servir à d'autres! wink

Code:

WITH

boyne AS (
    SELECT
        cdcourseau,
        st_linemerge(st_memunion(geom)) geom --fusion (st_memunion puis st_linemerge, mystérieux: une seule des 2 fonctions ne suffis pas!)
    from referentiel.cours_eau where topooh ilike '%boyne' -- sélection de la Boyne
    GROUP BY cdcourseau 
    )

,tmp AS (
    SELECT cdcourseau, (st_dump(geom)).path[1] as idln, (st_dump(geom)).geom
    from boyne )
--    from outils.cours_eau_simplifie ) --utilisé uniquement dans le cas de cours d'eau contenant des plans d'eau

,tmp1 AS (
    SELECT
        cdcourseau,
        idln,
        floor(st_length(geom) / 500.0)::int as numcut,
        (500/st_length(geom)) as frac_500m,
        geom
    FROM tmp)

,tmp2 as (
    SELECT
        t.*,
        g.cutpos,
        g.cutpos * frac_500m as startfraction,
        least((g.cutpos * frac_500m) + frac_500m , 1) as endfraction
    FROM tmp1 t
    CROSS JOIN LATERAL (
        SELECT cutpos
        FROM generate_series(0, t.numcut) as cutpos
        ) g )

SELECT t.cdcourseau,
        idln, numcut, frac_500m, startfraction, endfraction,
        st_linesubstring(geom, startfraction, endfraction) as geom,
         concat(cdcourseau,'-',to_char(row_number() over (),'FM000')) id_troncon
FROM tmp2 t
order by 1 --id_troncon

Mathieu
C'est en forgeant qu'on devient forgeron

Hors ligne

 

#8 Sat 18 September 2021 20:58

tumasgiu
Membre
Lieu: Ajaccio
Date d'inscription: 5 Jul 2010
Messages: 1160

Re: Couper une ligne tous les X mètres

Si votre cours d'eau est composé de plusieurs multilinestring dans votre base de données, alors cela parait logique de faire une union de celles-ci avant d'appliquer un linemerge sur l'ensemble.

Dernière modification par tumasgiu (Mon 20 September 2021 17:19)

Hors ligne

 

Pied de page des forums

Powered by FluxBB