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Ă© ?

#1 Wed 20 November 2024 11:10

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

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: 1226

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: 628

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: 1566

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: 1226

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: 1566

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: 1566

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: 1226

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: 1566

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: 628

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: 1226

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: 1566

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: 628

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: 1226

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: 3235
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

Copyright Association GeoRezo