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 Wed 13 December 2017 09:40

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

PostgreSQL/PostGIS : topologie et correction artéfacts intersections

Bonjour à tous,

Je travaille dans une BDD PostgreSQL (serveur 9.6, màj vers la v10 prévue, mais pas dans les délais impartis par mon projet) avec PostGIS (2.4), sous linux.

Suite à une série de découpages (ST_Intersection, ST_SymDifference, etc) j'ai une couche qui contient pas mal de micropolygones.
Ces polygones ont une géométrie valide.
Je souhaiterais dégager les polygones dont la surface est < 200m².
Jusque là c'est facile:


Code:

SELECT *
FROM ma_couche
WHERE ST_Area(geom) > 200

Par contre l'espace laissé vide entre 2 polygones ou au sein d'un même polygone, qui lui aussi est < 200m² rend mes polygones non jointifs ou "dégueulasses" (trous de moins de 200m² au sein d'un polygone).

Je cherche une solution "automatique" (un peu à la manière de ST_MakeValid pour les corrections de géométrie de PostGIS) qui permet de:
   - recoller les polygones s'ils sont disjoints de moins de 20m (par exemple)
   - combler les îles et anneaux minuscules créés par les découpages successifs


Je sens bien qu'il y a quelque chose à faire du côté de l'extension postgis_topology de postgreSQL (déjà installée sur mon serveur) mais je suis un béotien total en la matière!

Merci d'avance pour vos lumières! smile
Mathieu.


Mathieu
C'est en forgeant qu'on devient forgeron

Hors ligne

 

#2 Wed 13 December 2017 10:25

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

Re: PostgreSQL/PostGIS : topologie et correction artéfacts intersections

Bonjour,

Vaste problème que la correction topologique de couches surfaciques.
Postgis n'est pas vraiment fait pour ca, et n'a pas d'outils magiques pour corriger ces couches.
La topologie est une solution partielle en ce sens que la construction d'une topo plante souvent qd les geom en entrées ne sont pas valides ou meme pas clean topologiquement.
Il y a des méthodes pour créer la topologie dans une fonction plpgsql, puis attraper les erreurs et de tenter de corriger la geom qui pose probleme avant de la réinjecter dans la topo.
Le pb de la topo est que c'est super lent de la créer.

On peut aussi faire par fonctions spatiales, en regardant a quels polygones sont collés les petits polygones à nettoyer, et en les unissant à ces polygones.
Par ex pour les petits trous dans les polygones, il est facile de les unir au polygone qui les contient.

Perso, pour faire ca sur des couches importantes de parcelles, j'ai laché postgis (sniff sad ) et suis passé par GRASS 7: il a un module pour écrire des topologies postgis directement.

Le process:
charger la couche dans grass
nettoyer la topo (clean, autres)
charger le résultat dans une topologie postgis
reconstruire les objets geo a partir de la topo.

Comme je suis une tache avec GRASS, j'ai jamais réussi l'opération d'associer certains des petits polygones aux voisins, ce que sait faire grass.
Au final, ces petits pg giclent lors de l'export en topo postgis, ce qui allait bien pour mon traitement. Par contre, ca bouche les petits trous et les jointures sont bonnes => couche clean au niveau topologique

Nico

Hors ligne

 

#3 Wed 13 December 2017 11:45

ppluvinet
Participant assidu
Lieu: VALENCE
Date d'inscription: 6 Aug 2007
Messages: 617

Re: PostgreSQL/PostGIS : topologie et correction artéfacts intersections

On peut aussi faire par fonctions spatiales, en regardant a quels polygones sont collés les petits polygones à nettoyer, et en les unissant à ces polygones.


Il est effectivement possible avec PostGIS d'établir plusieurs règles pour associer un petit polygone à son voisin :
- associer aux voisins de + grandes tailles
- associer au voisins partageant le + de bordure commune
- associer en fonctions de certains attributs...

Il suffit donc par des UPDATE de modifier les attributs des petits polygones pour ensuite faire un ST_UNION et fusionner les parcelles adjacentes qui ont les même attributs.

Au préalable, on peut couper ces petits polygones en pleins de petit morceaux avec ST_Subdivide() , surtout si certains sont très longs et sont adjacents de + de deux polygones, cela évitera d'avoir comme résultat final des polygones en forme de casseroles !

J'ai déjà essayé des corrections topo avec GRASS, c'est généralement beaucoup plus rapide que PostGIS mais, de souvenir, le résultat n'est pas toujours satisfaisant. Pour celui qui a la bonne ligne de commande qui marche à tous les coups , je suis preneur !

Bonne continuation,


Pascal PLUVINET

Hors ligne

 

#4 Thu 14 December 2017 09:38

ChristopheV
Membre
Lieu: Ajaccio
Date d'inscription: 7 Sep 2005
Messages: 3199
Site web

Re: PostgreSQL/PostGIS : topologie et correction artéfacts intersections

Bonjour,

Pour la topologie je partage l'avis de Nicolat. Outre le fait que c'est "lent" à créer (il y a des astuces pour optimiser), au niveau d'une éventuelle correction la cela devient de la science fiction. J'ai tenté de supprimer automatiquement des arcs communs à des couples petites et grosses surfaces, un ça va mais plusieurs c'est la cata. En fait il faut faire un vaccum sur la base à chaque arc supprimé si on veut avoir qq chose de propre.
J'ai laissé tombé.
Par contre étant moi aussi un GRASS NULL je serai bien content d'étudier la piste .... des docs, tutos ?


Christophe
L'avantage d'être une île c'est d'être une terre topologiquement close

Hors ligne

 

#5 Thu 14 December 2017 09:38

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

Re: PostgreSQL/PostGIS : topologie et correction artéfacts intersections

Bonjour,
Un grand merci pour vos réponses.
Je vais creuser du côté de PostGIS (car je ne connais pas (encore?!) GRASS).
Il est probable que je vous sollicite à nouveau en cas de problème.
Comme Christophe, je reste bien entendu intéressé par des éventuels tutos existants.
Bonne journée,
Mathieu.

Dernière modification par Mathieu Denat (Thu 14 December 2017 09:40)


Mathieu
C'est en forgeant qu'on devient forgeron

Hors ligne

 

#6 Thu 14 December 2017 10:05

JP LLORENS
Participant assidu
Date d'inscription: 12 Nov 2008
Messages: 231

Re: PostgreSQL/PostGIS : topologie et correction artéfacts intersections

Bonjour.
J'ai eu un problème similaire sur une couche d'occupation du sol. Quand j'ai découpé cette couche avec les limites communales pour affecter un code insee, je me suis retrouvé avec des micros polygones. J'ai donc fait une fonction qui fusionnait ces micro polygone avec le polygone "père" mitoyen (qui a le même identifiant). C'est une boucle qui nettoie les polygones <1m².
Certes le résultat en sortie ne colle pas pil poil aux limites communales mais ça évacue un certains nombre de micro entités.
Ca donne ça : (la couche de départ se nomme ocs)

Code:

CREATE OR REPLACE FUNCTION public.cagv_clean_ocs()
  RETURNS character varying AS
$BODY$DECLARE
--detail_index text;
inf_zero record;
inf_max record;
id_min integer;
ogc_fid_max integer;
nb_enregistrements integer;
lageom_union geometry;

i integer;
j integer;

BEGIN

i=0;
nb_enregistrements = 0;

FOR inf_zero IN (select ogc_fid, wkb_geometry, id from ocs where surf_m2 < 1)
LOOP
    j=1;
    i = i+1;
    id_min = inf_zero.id;
    nb_enregistrements =  max(rank) from (select ogc_fid, surf_m2, rank()  over (partition by id order by surf_m2 desc) from ocs where id = inf_zero.id and surf_m2 >= 1 )foo;
    
    FOR inf_max IN (select ogc_fid, wkb_geometry, surf_m2, rank()  over (partition by id order by surf_m2 desc) from ocs where id = inf_zero.id and surf_m2 >= 1 )
    LOOP
        lageom_union = null;        
        IF j <= nb_enregistrements and ST_TOUCHES(inf_zero.wkb_geometry, inf_max.wkb_geometry) THEN
            lageom_union = ST_UNION (inf_zero.wkb_geometry, inf_max.wkb_geometry) from ocs where ogc_fid = inf_max.ogc_fid;
            IF geometrytype(lageom_union) = 'POLYGON' THEN        
                update ocs set wkb_geometry = lageom_union, surf_m2 =st_area(lageom_union) where ogc_fid = inf_max.ogc_fid;
                delete from ocs where ogc_fid = inf_zero.ogc_fid;
                EXIT;
            ELSE
                j = j+1;
            END IF;
        ELSE
            j = j+1;
        END IF;    
    END LOOP;

    

--raise notice 'le nombre: %',i;
END LOOP;
return     'ok'; 
END;$BODY$
  LANGUAGE plpgsql VOLATILE SECURITY DEFINER
  COST 100;
ALTER FUNCTION public.cagv_clean_ocs()
  OWNER TO postgres;
COMMENT ON FUNCTION public.cagv_clean_ocs() IS 'Fonction de nettoyage des micro polygones issus du st_intersections pour découper selon les limites admin et affecter le code INSEE';

C'est certainement améliorable mais ça peut être une base de départ pour vous
Cordialement
JP

Hors ligne

 

#7 Thu 14 December 2017 10:37

ppluvinet
Participant assidu
Lieu: VALENCE
Date d'inscription: 6 Aug 2007
Messages: 617

Re: PostgreSQL/PostGIS : topologie et correction artéfacts intersections

Bonjour, je me suis également replongé dans un vieux script que je n'ai pas retesté et qu'il faut sans doute améliorer.
Je le poste quasi tel quel :


Code:

---créer un nouvel identifiant
alter table shema.matable add column gidnew integer;
update shema.matable set gidnew = gid;

---créer la bordure  de chaque polygon  (st_exteriorRing ou essayer st_boundary ? )
alter table shema.matable add column geomext geometry;
update shema.matable set geomext = st_exteriorRing((st_dumpRings((st_dump(geom)).geom)).geom);

CREATE INDEX matable_geomext_indx on shema.matable using gist (geomext);
vacuum analyse shema.matable;

 
---On attribut au polygon < 200m2 , les identifiants des polygons voisisn de + 200m2 qui ont le plus de bordure commune

update shema.matable a set gidnew = foo.gidb , attribut1= foo.attribut1, attribut2 = foo.attribut2, 
attribut3 = foo.attribut3, attribut4 = foo.attribut4
    from
        (
        select distinct on (gida) gida , gidb, 
        attribut1, attribut2, attribut3, attribut4,
        st_length(geom)  as l, geom
        from 
            (select a.gid as gida, b.gid as gidb, 
             b.attribut1, b.attribut2, b.attribut3, b.attribut4,
            st_intersection(a.geomext,b.geomext) as geom 
            from shema.matable a,  shema.matable b
            where st_area2d(a.geom) < 200 and st_area2d(b.geom) >= 200   
            and a.geom && b.geom
            and st_relate(a.geomext, b.geomext, '1********')
             and a.gid != b.gid) 
         as inter
        order by gida, l desc
        ) as foo
        where gid = foo.gida
;

--Puis faire un ST_UNION

create table shema.matable2 as
select gidnew, attribut1,attribut2,attribut3,attribut4, st_union(geom) as geom
from shema.matable
group by gidnew, attribut1,attribut2,attribut3,attribut4;

Bonne continuation,

Dernière modification par ppluvinet (Thu 14 December 2017 10:37)


Pascal PLUVINET

Hors ligne

 

#8 Thu 14 December 2017 11:36

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

Re: PostgreSQL/PostGIS : topologie et correction artéfacts intersections

Bonjour,

A mon tour de filer du code big_smile

Pour cleaner des parcelles cadastrales par ex et générer une topologie clean:

Un shell qui execute du SQL postgis et du grass:

Code:

...
$PSQL $DBCON -c "SELECT topology.DropTopology('topo_grass')" -d $DBNAME
# lancement de grass en mode job batch, avec notre liste de commandes:
export GRASS_BATCH_JOB="${TMP_DIR}/clean_by_grass.bash"
echo "grass job: ${GRASS_BATCH_JOB}"
$GRASS $LOCATION

unset GRASS_BATCH_JOB
...

Un shell qui contient les commandes GRASS: clean_by_grass.bash:

Code:

#!/bin/bash
# nettoyage de la table parcelle et passage en topologie
########################################################################################################################
# Var utilisées
G_PG_CON="PG:dbname=${DBNAME} host=${DBHOST} port=${DBPORT} user=${DBUSER}"

echo "$TMP_TS: chargement de la table parcelle dans GRASS: supression de la couche grass parcelle ..."
g.remove -f type=vector name=parcelle@paris

v.in.ogr --overwrite snap=0.001 input="${G_PG_CON}" layer=parcenr.parcelle output=parcelle geometry=geom

TMP_TS=$(date +"%d-%m-%Y %H:%M:%S")
echo "$TMP_TS: chargement de la table parcelle dans GRASS: DONE"

# topo
TMP_TS=$(date +"%d-%m-%Y %H:%M:%S")
echo "$TMP_TS: construction de la topo postgis depuis grass___"

v.out.postgis --overwrite -l -2 \
    input=parcelle output="${G_PG_CON}" \
    output_layer=parcenr.parcelle_clean \
    options="SRID=2154,TOPOSCHEMA_NAME=topo_grass,TOPO_TOLERANCE=0.001,SPATIAL_INDEX=NO"

TMP_TS=$(date +"%d-%m-%Y %H:%M:%S")
echo "$TMP_TS: construction de la topo postgis depuis grass: DONE"

TMP_TS=$(date +"%d-%m-%Y %H:%M:%S")
echo "$TMP_TS: chargement parcelle clean avec geometrie depuis grass vers pg: DONE"

J'ai noté des pb de relations dans la topologie postgis écrite par GRASS, j'ai pas creusé, mais un update permet de régler cela:

Code:

-- grass ne produit pas de relation: on met a jour a partir de parcelle_clean pour
-- pouvoir exploiter la topo
insert into topo_grass.relation
  select (p.topo).id as topogeo_id, (p.topo).layer_id, f.face_id as element_id, (p.topo).type
    from parcenr.parcelle_clean p join topo_grass.face f on (p.topo).id = f.face_id;

Ensuite, et c'est là que c'est "tout pourri", je ne garde que le plus grand polygone parmi les polygones de meme id retournés par grass: les intersections entre parcelles (qui ne devraient pas exister) ont généré des petits polygones, mais attachés à une deux deux parcelles.
GRASS sait faire ca, et c'est même son but, c'est juste que j'ai compris/réussi à lancer les bonnes commandes (merger par catégorie par ex):

Code:

create table parcenr.parcelle as
  with tmp as (
      SELECT
        p.fid,
        p.id,
        p.idpar,
        p.code_insee,
        p.zonage,
        p.topo,
        topology.st_getFaceGeometry(topology.getTopologyName((p.topo).topology_id), (p.topo).id) AS geom
      FROM parcenr.parcelle_clean p
  ), tmp1 as (
  select t.*, row_number()
      OVER (PARTITION BY t.id
        ORDER BY st_area(t.geom) DESC) as rn
    from tmp t
  ) select t.id, t.idpar, t.code_insee, t.zonage, t.topo, t.geom
    from tmp1 t
  where rn = 1;

L'avantage est d'avoir au final une topologie clean des parcelles (exploitation des arcs pour trouver l'univers, les routes, les faces de batiments, etc...)
ET une table parcelle en objet qui est clean aussi, le tout chargé BEAUCOUP plus vite que dans Postgis: qq sec quand postgis met 20 min ou plus.

Nico

Hors ligne

 

#9 Thu 14 December 2017 11:38

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

Re: PostgreSQL/PostGIS : topologie et correction artéfacts intersections

(rho avec du temps ca serait marrant de prendre un dataset et de le tester sur chaque méthode, pour voir les avantages et inconvénients de chacune smile )

Hors ligne

 

#10 Wed 14 February 2018 18:37

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

Re: PostgreSQL/PostGIS : topologie et correction artéfacts intersections

Rebonsoir,

J'ai hésité à créer un nouveau sujet. Mais il m'a semblé que le sujet était lié aux difficultés rencontrées ici.

Mon script a fait son petit bonhomme de chemin, mais n'est pas terminé.
Je vous posterai les étapes que j'ai choisies comme retour et si j'ai le temps je nous anonymiserai un jeu de données pour qu'on s'amuse à comparer les différentes techniques (hein Nicolas! wink ).

Je ne suis pas sur de partir sur des choses très catholiques, mais c'est comme ça qu'on apprend!
Je suis donc preneur de retours sur ma méthode (j'ai choisi de ne pas utiliser d'autres fonctions que celles de PostGIS).

Et comme un schéma vaut mieux qu'un long discours, vous trouverez en pièce jointe une capture d'écran.
Voici quelques étapes qui m'en font baver.

En jaune clair, la couche A à triturer.
En bleu clair, la couche B à ne pas intersecter et à laquelle se "recoller".
En rouge le résultat de l'étape 1
En bleu foncé (lignes): résultat de l'étape 2.

Étape 1:

Code:

--extraction des limites des polygones ST_boundary
--segmentation en nombreux petits segments sT_Segmentize
--extraction des points ST_DumpPoints
--snapping des points à masque0 ST_Snap
SELECT DISTINCT
    ST_Snap(
        (ST_DumpPoints(ST_Segmentize(ST_Boundary(t.geom),2))).geom
        , f.geom, 2) geom
    , t.id_pk
FROM saisie.saisie_e1 t, masque0 f

Premier point que je n'arrive pas à régler. J'aimerais aimanter tous les points de A aux polygones B (y compris aux arrêtes des polygones et pas seulement aux sommets). Auriez-vous une piste de résolution?

Étape 2:

Code:

SELECT DISTINCT ST_ForceRHR(ST_MakeLine(geom)), id_pk
FROM etape1
GROUP BY 2

Je crée des lignes (ST_MakeLine) en vue de créer des polygones (ST_Polygonize), groupés selon les id_pk.
J'ai testé avec ou sans la fonction ST_ForceRHR(), je pensais qu'en forçant la "digitalisation main droite" j'arriverai à supprimer l'effet zig-zag que vous pouvez admirer sur ma PJ.
Mais cette fonction ne résout pas le pb, et ne change rien au résultat!
Deuxième point que je cherche à résoudre: faire comprendre à PostGIS qu'il doit créer des lignes en reliant les points les plus près entre eux, lorsqu'ils partagent le même id_pk. Auriez-vous là aussi des pistes de résolution?

Merci d'avance et bonne soirée.

Dernière modification par Mathieu Denat (Wed 14 February 2018 18:37)


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

Mathieu
C'est en forgeant qu'on devient forgeron

Hors ligne

 

#11 Thu 15 February 2018 16:42

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

Re: PostgreSQL/PostGIS : topologie et correction artéfacts intersections

Bonjour,

Le problème en snappant les points de la couche à corriger, c'est que même en densifiant le contour a 2m, les risques d'intersection entre les contours de deux polygones sont toujours possibles.

Dans cet exemple, le plus simple pour avoir des pg jointifs est de faire la différence entre les deux PG.

Sinon pour snapper sur le contour du PG, vous pouvez utiliser st_lineLocatePoint et st_lineInterpolatePoint (référencement linéaire)

Nicolas

Hors ligne

 

Pied de page des forums

Powered by FluxBB