#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: 1566
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: 1566
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
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 ![]()
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! ![]()
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_tronconDernière modification par Mathieu Denat (Fri 17 September 2021 12:01)
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: 1218
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é! ![]()
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! ![]()
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_tronconMathieu
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: 1218
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


