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 20 November 2024 11:10

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

UPDATE avec un WITH RECURSIVE

Bonjour,
J'ai une couche de polygones dont certains petits polygones ont un attribut NULL.
J'aimerai affecter à ces petits polygones la valeur d'un des polygones voisins. Comme certains sont entourés eux-mêmes de petits polygones avec des valeurs nulles, je souhaitais utiliser une fonction récursive qui ferait un UPDATE tant qu'il y a des valeurs nulles.
J'ai tenté la rédaction ci-après mais ca ne met à jour qu'une fois les polygones , la récursivité ne fonctionne pas et il reste des polygones avec des valeurs nulles qui ne sont pourtant pas isolés.
N'étant pas à l'aise avec les fonctions récursives, je fais appel à vos compétences

Code:

WITH RECURSIVE b AS
(
    SELECT * FROM poly
    WHERE attr is not null

)
UPDATE poly a
SET attr = b.attr
FROM  b 
WHERE a.attr is null  AND st_intersects(a.geom,b.geom)
;

Merci d'avance pour vos suggestions,

Dernière modification par ppluvinet (Wed 20 November 2024 11:54)


Pascal PLUVINET

Hors ligne

 

#2 Wed 20 November 2024 12:00

tumasgiu
Membre
Lieu: Ajaccio
Date d'inscription: 5 Jul 2010
Messages: 1160

Re: UPDATE avec un WITH RECURSIVE

Salut,

Il serait plus facile je pense d'écrire une fonction pl/psql pour arriver à ce que vous voulez faire.
Cela dit, voilà une idée de requête qui pourrait peut être fonctionner :

Code:

WITH RECURSIVE r(id, geom, attr) AS
(
    SELECT --partie de base de la requête recursive : on récupére tous les polygones sans attributs ayant un voisin avec attribut
        a.id,
        a.geom
        b.attr
    FROM 
        poly a 
    JOIN 
        poly b
    on  a.attr is null and b.attr is not null
        and st_intersects(a.geom, b.geom)
    UNION ALL
    SELECT --partie recursive : on récupére les enregistrements sans attributs voisins des enregistrements produits par le cas de base
        a.id,
        a.geom,
        r.attr
    FROM
        poly a
    RIGHT JOIN r
        ON a.id <> r.id --on veut éviter de reprendre les polygones déja traités
        and a.attr is null and r.attr is not null 
        and st_intersects(a.geom, b.geom)
),
x as (
    select * from r LIMIT 1000 -- on mets généralement une limit au resultat produit par une requête recursive
                                                 -- ca permet de tester qu'elle fonctionne et d'éviter qu'elle ne tourne indéfiniment
                                                 -- si on a mal ecrit la requête
)


UPDATE poly a
SET attr = x.attr
FROM  (select id,max(attr) from x GROUP BY id) x
WHERE 
    a.id = x.id
;

Les CTE récursives se composent généralement d'une partie de base (dans le code la requête au dessus du UNION ALL) et d'une partie recursive (sous le UNION ALL). La CTE s'arrête une fois qu'elle ne produit plus de nouveaux resultat (une nouvelle itération de la partie recursive ne renvoie pas d'enregistrement).

Une difficulté de votre demande vient du fait qu'il peut potentiellement y avoir plusieurs candidats pour chaque polygone sans attribut. Les GROUP BY et LATERAL JOIN n'étant pas autorisés dans les CTE recursives, la solution que je propose utilise la CTE pour générer toutes les combinaisons possibles et la sous requête de la clause FROM de la commande UPDATE selectionne la valeur à attribuer (ici j'ai choisi la fonction max mais on peut en choisir une autre ou faire cela de façon non déterministe).

Je ne suis pas sûr des bonnes performances de la requête, si elle fonctione.

Dernière modification par tumasgiu (Wed 20 November 2024 12:05)

Hors ligne

 

#3 Wed 20 November 2024 12:22

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

Re: UPDATE avec un WITH RECURSIVE

Merci beaucoup pour avoir pris le temps de me répondre.

Il faudra que je l'adapte encore car mon problème est un peu plus complexe. En effet, prendre la valeur du voisin est un peu trop simpliste !!

Je testerai et ferai un retour. Grand merci,


Pascal PLUVINET

Hors ligne

 

#4 Wed 20 November 2024 13:12

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

Re: UPDATE avec un WITH RECURSIVE

Bonjour,

Si les polygones à corriger intersectent des voisins dont ils doivent prendre une valeur, pourquoi ne pas utiliser st_clusterIntersecting ?
Vous aurez des groupes de polygones qui se touchent et dans chaque groupe, les polygones avec attribut null et des voisins avec l'attribut que voulez prendre.

(je sens bien que je n'ai pas tout compris à votre pb big_smile )

Nicolas

Hors ligne

 

#5 Wed 20 November 2024 13:15

tumasgiu
Membre
Lieu: Ajaccio
Date d'inscription: 5 Jul 2010
Messages: 1160

Re: UPDATE avec un WITH RECURSIVE

@Nicolas,

de ce que j'ai cru comprendre il y a des polygones dont aucun voisin ne possède d'attribut, d'ou l'approche itérative.

Hors ligne

 

#6 Wed 20 November 2024 13:18

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

Re: UPDATE avec un WITH RECURSIVE

Vous auriez un exemple visuel ?

Nicolas

Hors ligne

 

#7 Wed 20 November 2024 13:21

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

Re: UPDATE avec un WITH RECURSIVE

tumasgiu a écrit:

@Nicolas,

de ce que j'ai cru comprendre il y a des polygones dont aucun voisin ne possède d'attribut, d'ou l'approche itérative.


Ok merci pour la précision. Si ce polygone n'a que des voisins sans attribut, alors le voisin du voisin sera connecté avec un polygone ayant un attribut, qui apparaitra donc dans le cluster et dont on pourra recupérér l'attribut, non ?

Nico

Hors ligne

 

#8 Wed 20 November 2024 13:32

tumasgiu
Membre
Lieu: Ajaccio
Date d'inscription: 5 Jul 2010
Messages: 1160

Re: UPDATE avec un WITH RECURSIVE

Nicolas Ribot a écrit:
tumasgiu a écrit:

@Nicolas,

de ce que j'ai cru comprendre il y a des polygones dont aucun voisin ne possède d'attribut, d'ou l'approche itérative.


Ok merci pour la précision. Si ce polygone n'a que des voisins sans attribut, alors le voisin du voisin sera connecté avec un polygone ayant un attribut, qui apparaitra donc dans le cluster et dont on pourra recupérér l'attribut, non ?
Nico


Tout à fait. Je pense que l'approche avec st_clusterintersectings est valide. Après cela depend peut être de la façon dont on veut que les attributs se "diffusent" de voisin en voisin ? Il faudrait plus de précision sur le problème

En apparté je viens de voir que les fonctions de cluster ont maintenant une version utilisable dans les window function, smile.

Dernière modification par tumasgiu (Wed 20 November 2024 13:34)

Hors ligne

 

#9 Wed 20 November 2024 15:56

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

Re: UPDATE avec un WITH RECURSIVE

tumasgiu a écrit:

En apparté je viens de voir que les fonctions de cluster ont maintenant une version utilisable dans les window function, smile.


Ouiiii trop bien (et aussi les nouvelles fonctions de coverage, pour tester et cleaner ses couches)

Hors ligne

 

#10 Thu 21 November 2024 09:34

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

Re: UPDATE avec un WITH RECURSIVE

Bonjour,
Je n'ai pas encore testé la solution avec WITH RECURSIVE. Je vais m'y mettre ce matin !

Pour mieux expliquer mon problème de base : A l'intérieur d'un même ensemble de polygones jointifs, je peux avoir des grands polygones qui ont des valeurs d'attributs différents et une multitude de petits polygones avec des valeurs nulles. Certains de ces petits polygones sont entourés de petits polygones  avec des valeurs nulles aussi. Du coup, je doute qu'on puisse utiliser st_clusterintersectings (que je n'ai jamais utilisé par ailleurs ! ).
Ce que je souhaitais c'est de boucler pour que les valeurs nulles soient, petit à petit, remplacées par une des valeurs des voisins.

Si je veux aller au bout de ma démarche, je dois exécuter cette "diffusion récursive" à plusieurs endroits dans mon script. Peut-être qu'une fonction plpsql serait plus appropriées ?  Mais là aussi , il faut que je me repenche sur ce type de prog, car ca fait quelques années que je n'ai pas pratiqué ! Si vous avez quelque chose de simple pour démarrer ?

Merci d'avance,


Pascal PLUVINET

Hors ligne

 

#11 Thu 21 November 2024 10:38

tumasgiu
Membre
Lieu: Ajaccio
Date d'inscription: 5 Jul 2010
Messages: 1160

Re: UPDATE avec un WITH RECURSIVE

Si vous n'avez pas besoin de choisir quelle valeur d'attribut affecter à vos petits polygones, l'approche de Nicolas sera beaucoup moins casse-tête et sans doute plus rapide :

Pour chaque cluster de polygones renvoyé par la fonction st_clusterintersecting (elle renvoie un tableau de geometrycollection ou chaque entrée est un cluster), vous récupérez l'attribut d'un des polygones du cluster et vous l'affecter aux autres polygones sans attributs du cluster.

Hors ligne

 

#12 Thu 21 November 2024 11:01

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

Re: UPDATE avec un WITH RECURSIVE

ppluvinet a écrit:

Pour mieux expliquer mon problème de base : A l'intérieur d'un même ensemble de polygones jointifs, je peux avoir des grands polygones qui ont des valeurs d'attributs différents et une multitude de petits polygones avec des valeurs nulles. Certains de ces petits polygones sont entourés de petits polygones  avec des valeurs nulles aussi. Du coup, je doute qu'on puisse utiliser st_clusterintersectings (que je n'ai jamais utilisé par ailleurs ! ).
Ce que je souhaitais c'est de boucler pour que les valeurs nulles soient, petit à petit, remplacées par une des valeurs des voisins.


Un sample, un sample ! wink

Nico

Hors ligne

 

#13 Thu 21 November 2024 11:15

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

Re: UPDATE avec un WITH RECURSIVE

Le WITH RECURSIVE de tumasgiu fonctionne mais il est long et je n'ai pas manière de savoir où j'en suis. De plus, il y a toujours quelques polygones qui ont des valeurs nulles et il faudrait mettre une valeur bien plus importante au LIMIT.

J'ai donc tenté d'écrire une fonction qui semble fonctionner :


Code:

CREATE OR REPLACE function  diffusion() returns text
LANGUAGE plpgsql 
AS $$ 
DECLARE 
    re text default '';
    i INT := 1;
    BeforeMaj INT := 1;
    AfterMaj INT := 0;    
    NBPOLYUPDATED INT := 0;
BEGIN 
  WHILE AfterMaj < BeforeMaj LOOP 
    RAISE NOTICE 'i: %', i;
    BeforeMaj := (select count(*) from poly where attr is null);

    execute 'update poly a
    set attr = b.attr
    from poly b 
    where a.attr is null and b.attr is not null and st_intersects(a.geom,b.geom)'
    ;
    
    AfterMaj := (select count(*) from poly where attr is null);
    i := i+1;
    NBPOLYUPDATED := NBPOLYUPDATED + BeforeMaj - AfterMaj;
    RAISE NOTICE 'UPDATE: %', BeforeMaj - AfterMaj;
  END LOOP;
  re := 'NB TOTAL UPDATED: '|| NBPOLYUPDATED    ;
  return re;
END; 
$$ 
;

select diffusion();

Je vais pouvoir facilement complexifier le WHERE du UPDATE en contraignant d'avantage le choix du voisin.

Merci encore,


Pascal PLUVINET

Hors ligne

 

#14 Thu 21 November 2024 12:00

tumasgiu
Membre
Lieu: Ajaccio
Date d'inscription: 5 Jul 2010
Messages: 1160

Re: UPDATE avec un WITH RECURSIVE

ppluvinet a écrit:

Le WITH RECURSIVE de tumasgiu fonctionne mais il est long


Helas c'était à craindre.

ppluvinet a écrit:

il faudrait mettre une valeur bien plus importante au LIMIT.


Vous pouvez tout à fait augmenter le nombe d'enregistrement à retourner. Le limit n'est la que pour vous prémunir d'une possible requête qui ne terminerait jamais et saturerait votre serveur.


Votre code est bien, mais je pense que vous pouvez le simplifier comme suit (non testé) :

Code:

CREATE OR REPLACE function  diffusion() returns text
LANGUAGE plpgsql 
AS $$ 
DECLARE 
    updated := 1
    NBPOLYUPDATED INT := 0;
BEGIN 

  WHILE updated > 0 LOOP 

    RAISE NOTICE 'i: %', i;

    EXECUTE 'update poly a
    set attr = b.attr
    from poly b 
    where a.attr is null and b.attr is not null and st_intersects(a.geom,b.geom)'
    ;

    GET DIAGNOSTICS updated := ROW_COUNT ;
    --https://www.postgresql.org/docs/current/plpgsql-statements.html#PLPGSQL-STATEMENTS-DIAGNOSTICS

    NBPOLYUPDATED  := updated + NBPOLYUPDATED ;

  END LOOP;

   return 'NB TOTAL UPDATED: '|| NBPOLYUPDATED   ;

END; 
$$ 
;

select diffusion();

Il n'y a plus qu'a tester la version de Nicolas smile

Dernière modification par tumasgiu (Thu 21 November 2024 14:20)

Hors ligne

 

#15 Mon 25 November 2024 15:54

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

Re: UPDATE avec un WITH RECURSIVE

Bonjour,

En utilisant la topologie sous PostGis le problème se simplifie.

Très facile de trouver les faces voisines possédant une valeur = Null à une face donnée.
Ou inversement de connaître la voisine attribut non Null de face attribut=Null.

Une autre approche non testée, et l'utilisation des LATERAL JOIN.
Pour chaque polygone "non null" trouver les polygones adjacents de valeur nulle. Un st_touches() AND attribut isnull ou plutôt st_overlaps() AND attribut isnull.

[Edit] Je n'avais pas vu la condition "certains polygones de valeur NULL sont entourés de polygones valeurs Null". Dans ce cadre (je ne suis pas sur de modéliser tous les cas d'utilisation) je chercherais tous les polygones de valeur null adjacents, j'en ferais un multypolygone, ensuite je cherche à quel polyone de valeur non nulle ce multipolygon est adjacent, pour affecter le bon attribut à chaque partie du multipolygon. [Edit]

Dernière modification par ChristopheV (Tue 26 November 2024 12:09)


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

Hors ligne

 

Pied de page des forums

Powered by FluxBB