Prendre un thé
Tapoter des doights
Monsieur fantôme
Quelle heure est-il ?
BazinGa's Tips & tuto IT
BazinGa's - Tips & tuto IT

Créer des points ou des segments à distance régulière le long d’une ligne

Lorsqu’on travaille avec des linéaires, il peut arriver qu’on ait besoin de générer des points ou des segments à intervalles réguliers le long de ces lignes.

PostGIS possède plusieurs fonctions intéressantes qui permettent de réduire des géométries linéaires :

  • ST_DumpPoints : créé un ensemble de points composé des sommets constituant la ligne. L’intervalle n’est donc pas régulier.
  • ST_Segmentize : créé une multiligne dont la longueur de chaque membre ne dépasse pas une variable. Ne créé pas de points car la longueur maximum doit être supérieur à 0.
  • ST_Subdivide : créé un ensemble de ligne dont le nombre de sommet est inférieur à une variable. Ne créé pas de points car le nombre minimum de sommet doit être supérieur à 5.

Ainsi, il est nécessaire de passer par un petit bricolage maison pour générer ces points/segments.
Nous allons donc pour cela utiliser les fonctions generate_series et ST_LineInterpolatePoint .

Premier cas simple :

SELECT 
	id,
	stat_point.numero,
	stat_point.position_cumul_ligne,
	stat_point.position_cumul_ligne - stat_point.position_cumul_ligne_precedent AS distance_point_precedent,
	stat_point.position_pourcentage_ligne,
	-- Création du point sur la ligne
	ST_LineInterpolatePoint(geom, stat_point.position_pourcentage_ligne) AS  geom
FROM 
	mon_schema.ma_table_de_ligne,
	-- Jointure LATERAL permetant de réutiliser les éléments présents dans la jointure précédente
	LATERAL ( 
		SELECT
			-- Définition de l'intervalle pas entre chaque points (ici 5 mètres)
			5 AS step,
			-- Longueur de la ligne
			st_length(geom) AS longueur_sig
	) AS variables,
	LATERAL (
		SELECT
			-- Nombre de points sur la ligne : longueur de la ligne divisé par le pas, le tout arrondi à l'entier supérieur (le dernier point coïncide à l'extrémité de la ligne)
			ceil(variables.longueur_sig / variables.step)::NUMERIC AS nb_point,
			-- Génération d'une série 
			-- de 0 au nombre de points sur la ligne 
			generate_series(0, ceil(variables.longueur_sig / variables.step)::NUMERIC, 1) AS numero 
	) AS serie,
	LATERAL (
		SELECT 
			-- Numéro d'ordonancement du point
			serie.numero,
			-- Position du point sur la ligne en pourcentage de la longueur
			CASE 
				WHEN serie.numero = serie.nb_point THEN 1
				ELSE ( serie.numero * variables.step ) / variables.longueur_sig 
			END AS position_pourcentage_ligne,
			-- Position du point en m par rapport au début de la ligne
			CASE 
				WHEN serie.numero = serie.nb_point THEN variables.longueur_sig
				ELSE ( serie.numero * variables.step )
			END AS position_cumul_ligne,
			-- Position du point précédent en m par rapport au début de la ligne
			CASE 
				WHEN serie.numero = 0 THEN 0
				ELSE ( ( serie.numero - 1 ) * variables.step )
			END AS position_cumul_ligne_precedent
	) AS stat_point
SELECT 
	id,
	stat_segment.numero,
	stat_segment.position_deb_pourcentage_ligne,
	stat_segment.position_fin_pourcentage_ligne,
	stat_segment.position_deb_cumul_ligne,
	stat_segment.position_fin_cumul_ligne,
	-- Création du segment sur la ligne
	st_linesubstring(
		geom,
		stat_segment.position_deb_pourcentage_ligne,
		stat_segment.position_fin_pourcentage_ligne
	) AS  geom
FROM 
	mon_schema.ma_table_de_ligne,
	-- Jointure LATERAL permetant de réutiliser les éléments présents dans la jointure précédente
	LATERAL ( 
		SELECT
			-- Définition de la longueur de chaque segment (ici 10 mètres)
			5 AS step,
			-- Longueur de la ligne
			st_length(geom) AS longueur_sig
	) AS variables,
	LATERAL (
		SELECT
			-- Nombre de segment sur la ligne : longueur de la ligne divisé par le pas, le tout arrondi à l'entier inférieur (le dernier segment ira jusqu'au bout de la ligne et fera moins que la longueur définie)
			floor(variables.longueur_sig / variables.step)::NUMERIC AS nb_segment,
			-- Génération d'une série 
			-- de 0 au nombre de segments sur la ligne
			generate_series(0, floor(variables.longueur_sig / variables.step)::NUMERIC, 1) AS numero 
	) AS serie,
	LATERAL (
		SELECT 
			-- Numéro d'ordonancement du segment
			serie.numero,
			-- Position du début du segment sur la ligne en pourcentage de la longueur
			( serie.numero * variables.step ) / variables.longueur_sig AS position_deb_pourcentage_ligne,
			-- Position de la fin du segment sur la ligne en pourcentage de la longueur
			CASE 
				WHEN serie.numero = serie.nb_segment THEN 1
				ELSE ( (serie.numero + 1) * variables.step ) / variables.longueur_sig 
			END AS position_fin_pourcentage_ligne,
			-- Position du début du segment en m par rapport au début de la ligne
			serie.numero * variables.step AS position_deb_cumul_ligne,
			-- Position de la fin du segment en m par rapport au début de la ligne
			CASE 
				WHEN serie.numero = serie.nb_segment THEN variables.longueur_sig
				ELSE ( (serie.numero + 1) * variables.step )
			END AS position_fin_cumul_ligne
	) AS stat_segment

Second cas plus complexe : un ensemble de lignes calibrées.
Pour chaque ligne, un cumul de début et un cumul de fin est défini. Ce cas peut arriver lorsque l’on travail avec des relevés routiers ou des linéaires de cours d’eau.

SELECT 
	colonne_id,
	user_data.cumul_initial,
	user_data.cumul_final,
	user_data.step,
	data_line.cumul_initial,
	data_line.cumul_final,
	serie.longueur_sig,
	serie.longueur_calibre,
	serie.point_cumul_sur_ligne,
	stat.point_cumul_calibre,
	stat.point_ordre,
	stat.pourcentage_ligne,
	-- Création du point sur la ligne
	ST_LineInterpolatePoint(geom, stat.pourcentage_ligne) AS  geom
FROM 
	mon_schema.ma_table,
	-- Jointure LATERAL permetant de réutiliser les éléments présents dans la jointure précédente
	LATERAL ( 
		SELECT
			-- Définition du cumul initial de la ligne (en général une colonne contient cette donnée)
			0 AS cumul_initial,
			-- Définition du cumul final de la ligne (en général une colonne contient cette donnée)
			0 AS cumul_final,
			-- Définition de l'intervalle pas entre chaque points (ici 3 mètres)
			3 AS step
	) AS user_data,
	LATERAL ( 
		SELECT
			-- Nettoyage du cumul initial (permet d'utiliser la valeur '0' s'il n'existe pas de cumul initial)
			CASE 
				WHEN user_data.cumul_initial IS NOT NULL
				AND user_data.cumul_initial::numeric <> 0 
					THEN user_data.cumul_initial::numeric
				ELSE 0::numeric
			END AS cumul_initial,
			-- Nettoyage du cumul final (permet d'utiliser la valeur '0' s'il n'existe pas de cumul final : dans ce cas on utilise la longueur de la ligne)
			CASE 
				WHEN user_data.cumul_final IS NOT NULL
				AND user_data.cumul_final::numeric <> 0 
				AND user_data.cumul_final::numeric > user_data.cumul_initial::numeric
					THEN user_data.cumul_final::numeric
				ELSE st_length("GEOM")::numeric
			END AS cumul_final
	) AS data_line,
	LATERAL (
		SELECT
			-- Longueur de la ligne
			st_length(geom) AS longueur_sig,
			-- Longueur calibrée
			data_line.cumul_final - data_line.cumul_initial AS longueur_calibre,
			-- Génération d'une série de 0 à la valeur de longueur calibrée de la ligne avec un pas défini précédement
			generate_series(0, data_line.cumul_final - data_line.cumul_initial, user_data.step) AS point_cumul_sur_ligne
	) AS serie,
	LATERAL (
		SELECT 
			-- Numero d'ordre du point
			( serie.point_cumul_sur_ligne / user_data.step ) AS point_ordre,
			-- Positionnement du point sur la ligne en cumul calibré
			serie.point_cumul_sur_ligne + data_line.cumul_initial AS point_cumul_calibre,
			-- Positionnement du point sur la ligne en pourcentage de la longueur
			serie.point_cumul_sur_ligne / serie.longueur_calibre AS pourcentage_ligne
	) AS stat

Cet article vous a plu ?

N'hésitez pas à le partager, il interessera surement certains de vos contacts.

Les thèmes suivants contiennent des articles en lien avec celui-ci, allez faire un tour :

BDDPostGISPostgreSQLProgrammationSIGSQL georeferencement linéairepointsegmentationsérie

50%