Accueil / Blog / Métier / 2013 / Draper des lignes sur un MNT avec PostGIS

Draper des lignes sur un MNT avec PostGIS

Par Mathieu Leplatre publié 15/10/2013, édité le 17/06/2014
Comment obtenir des géométries 3D en drapant des géométries sur un MNT avec PostGIS 2
Draper des lignes sur un MNT avec PostGIS

Dans Geotrek les géométries des tronçons sont saisies en 2D, puis drapées en 3D, afin d'obtenir des informations relatives à l'altimétrie (pente, dénivelées, ...)

Nous présentons ici les fonctions PostGIS utilisées pour y parvenir.

Charger le MNT

Si votre Modèle Numérique de Terrain (MNT) est compatible avec GDAL, vous pouvez alors charger le raster très facilement dans la base de données avec les commandes suivantes.

Reprojette vers le SRID spécifié, rogne sur l'étendue spécifiée, et écrit le résultat dans un fichier binaire:

gdalwarp -t_srs EPSG:32632 -te 289942 4845809 400671 4947295 dem_file.geotif output.bin

Découpe en tuiles de 100 pixels et convertit en instructions SQL:

raster2pgsql -c -C -I -M -t 100x100 output.bin mnt > output.sql

Charge le SQL dans la base :

psql -d yourdb < output.sql
/blog/metier/postgis_dem_qgis/image

Draper les géométries

Il y a pour le moins 3 stratégies pour draper les géométries vers la 3D.

En gardant la résolution des géométries

On obtient une altitude pour chaque point de la ligne.

Avantages: On garde le niveau de définition original (nombre de points)

Inconvénients: On perd potentiellement beaucoup d'information ("sauts")

/blog/metier/postgis_dem_native/image
 WITH line AS
    -- Prendre une ligne arbitraire
    (SELECT 'SRID=32632;LINESTRING (348595 4889225,352577 4887465,354784 4883841)'::geometry AS geom),
  points2d AS
    -- Extraire ses points
    (SELECT (ST_DumpPoints(geom)).geom AS geom FROM line),
  cells AS
    -- Obtenir l'altitude pour chacun d'eux
    (SELECT p.geom AS geom, ST_Value(mnt.rast, 1, p.geom) AS val
     FROM mnt, points2d p
     WHERE ST_Intersects(mnt.rast, p.geom)),
    -- Instancier des points 3D
  points3d AS
    (SELECT ST_SetSRID(ST_MakePoint(ST_X(geom), ST_Y(geom), val), 32632) AS geom FROM cells)
-- Contruire la ligne 3D depuis les points 3D
SELECT ST_MakeLine(geom) FROM points3d;

Selon la résolution du MNT

On obtient une altitude pour chaque cellule du raster.

Avantages: On profite au maximum des informations du MNT

Inconvénients: On augmente énormément la résolution des géométries, parfois pour rien.

/blog/metier/postgis_dem_full/image
 WITH line AS
    -- Prendre une ligne arbitraire
    (SELECT 'SRID=32632;LINESTRING (348595 4889225,352577 4887465,354784 4883841)'::geometry AS geom),
  cells AS
    -- Obtenir l'altitude pour chaque cellule intersectée
    (SELECT ST_Centroid((ST_Intersection(mnt.rast, line.geom)).geom) AS geom,
    (ST_Intersection(mnt.rast, line.geom)).val AS val
     FROM mnt, line
     WHERE ST_Intersects(mnt.rast, line.geom)),
    -- Instancier des points 3D, dans l'ordre de la ligne
  points3d AS
    (SELECT ST_SetSRID(ST_MakePoint(ST_X(cells.geom), ST_Y(cells.geom), val), 32632) AS geom
     FROM cells, line
     ORDER BY ST_Distance(ST_StartPoint(line.geom), cells.geom))
-- Construire la ligne 3D depuis les points 3D
SELECT ST_MakeLine(geom) FROM points3d;

En échantillonnant

On obtient une altitude par pas de X unités (mètres).

Avantages: On controle la résolution des géométries obtenues

Inconvénients: Parfois difficile un bon compromis selon la longueur des géométries

/blog/metier/postgis_dem_sampled/image
 WITH line AS
    -- Prendre une ligne arbitraire
    (SELECT 'SRID=32632;LINESTRING (348595 4889225,352577 4887465,354784 4883841)'::geometry AS geom),
  linemesure AS
    -- Ajouter une mesure pour découper selon un pas (ici, de 50)
    (SELECT ST_AddMeasure(line.geom, 0, ST_Length(line.geom)) as linem,
            generate_series(0, ST_Length(line.geom)::int, 50) as i
     FROM line),
  points2d AS
    (SELECT ST_GeometryN(ST_LocateAlong(linem, i), 1) AS geom FROM linemesure),
  cells AS
    -- Obtenir l'altitude pour chacun d'eux
    (SELECT p.geom AS geom, ST_Value(mnt.rast, 1, p.geom) AS val
     FROM mnt, points2d p
     WHERE ST_Intersects(mnt.rast, p.geom)),
    -- Instancier des points 3D
  points3d AS
    (SELECT ST_SetSRID(ST_MakePoint(ST_X(geom), ST_Y(geom), val), 32632) AS geom FROM cells)
-- Construire la ligne 3D
SELECT ST_MakeLine(geom) FROM points3d;

En tant que fonction PostgreSQL

Il est possible d'enfermer tout ce code dans une fonction :

CREATE OR REPLACE FUNCTION drape(line geometry) RETURNS geometry AS $$
DECLARE
    line3d geometry;
BEGIN
    WITH ...
         ...
         ...
         ...
    SELECT ST_MakeLine(geom) INTO geom3d FROM points3d;
    RETURN geom3d;
END;
$$ LANGUAGE plpgsql;

Et draper les géométries d'une table:

-- Ajouter une colonne
ALTER TABLE yourtable ADD COLUMN geom_3d geometry(LineStringZ, 32632);

-- geom_3d avec la version drapée
UPDATE yourtable SET geom_3d = drape(geom);
ABONNEZ-VOUS À LA NEWSLETTER !
Voir aussi
PostgreSQL utilisations avancées de generate_series pour générer du contenu PostgreSQL utilisations avancées de generate_series pour générer du contenu 27/06/2017

Générer du contenu en masse permet de tester les requêtes, index et traitements complexes sur ...

Bien débuter avec les transactions SQL Bien débuter avec les transactions SQL 09/12/2015

BEGIN et COMMIT vous connaissez, mais ACID ou LOCK ça vous dit quelque chose ?

Les curseurs PostgreSQL 11/06/2015

Découvrons comment utiliser les curseurs PostgreSQL pour effectuer des requêtes renvoyant de ...

Formation Python avancé du 12 au 16 juin à Paris Formation Python avancé du 12 au 16 juin à Paris 12/04/2017

Pour les développeurs qui veulent approfondir leur connaissance du langage Python : de la ...

Geotrek - 1ère rencontre des utilisateurs 21/12/2016

La communauté Geotrek, regroupant environ 60 personnes, s'est retrouvée le 18 octobre 2016 à ...