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