#1 Thu 20 February 2014 12:29
- SANTANNA
- Moderateur
- Lieu: Angers
- Date d'inscription: 18 Jan 2008
- Messages: 3945
Trigger avec st_contains et sommets différents
Bonjour,
J'ai une fonction trigger before que j'utilise pour récupérer sur des entités le numéro de la parcelle qui est en dessous. Généralement, (sous QGIS) on copie-colle la parcelle et on en modifie la géométrie si besoin est (jamais en allant au-delà des limites de la parcelle). voici le bout de code qui m'interpelle
Code:
CREATE OR REPLACE FUNCTION foncier.insert_site() RETURNS trigger AS $BODY$BEGIN if new.num_parcelle is null or new.num_parcelle='' then select into new.num_parcelle id_par from cadastre.parcelle where st_contains(geom, new.geom) end if; return new; END; $BODY$ LANGUAGE plpgsql VOLATILE COST 100;
Or, je constate un comportement bizarre qui me fait m'interroger sur la pertinence de la fonction st_contains dans le cas présent.
Lorsqu'on modifie (sous QGIS) la géométrie de la nouvelle entité et que ses sommets correspondent toujours à celle de la parcelle ou que ses sommets sont clairement à l'intérieur de la parcelle, ça marche. Par contre, si on crée un seul nouveau sommet sur un des segments de la parcelle, là, le trigger ne fonctionne pas.
Quand je lis dans la doc de PostGIS
ST_Contains — Returns true if and only if no points of B lie in the exterior of A, and at least one point of the interior of B lies in the interior of A
, je me demande quelle est la place de la frontière dans ce cas? Le nouveau sommet de mon entité est-il considéré à l'intérieur ou à l'extérieur de la parcelle? Et quelle(s) fonction(s) semblent donc plus appropriée(s) à ma requête?
Une autre question que je souhaiterais éclaircir, si possible: quelle est la différence (performance, traitements effectués ...) entre st_contains(geomA,geomB) et le couple geomA && geomB and _st_contains(geomA,geomB) ?
Merci par avance,
Santanna
Hors ligne
#2 Thu 20 February 2014 13:51
- VianneyD
- Participant assidu
- Date d'inscription: 30 May 2011
- Messages: 153
Re: Trigger avec st_contains et sommets différents
Bonjour,
En regardant de plus près l'exemple donné dans la doc, on lit que ST_Contains(geomA, ST_Boundary(geomA)) renvoie false, j'aurais donc tendance à penser que la frontière est considérée comme non contenue dans la géométrie.
Ca a l'air d'être une subtile différence avec ST_Covers et ST_Within... Peut-être auras-tu plus de succès avec l'une de ces dernières...
Vianney Dugrain
Hors ligne
#3 Thu 20 February 2014 15:29
- SANTANNA
- Moderateur
- Lieu: Angers
- Date d'inscription: 18 Jan 2008
- Messages: 3945
Re: Trigger avec st_contains et sommets différents
Merci Vianney de te pencher sur la question
Il me semble que ST_WITHIN n'est que l'écriture inversée de ST_CONTAINS
ST_Contains is the inverse of ST_Within. So ST_Contains(A,B) implies ST_Within(B,A)
En lisant plus en détails la page ST_COVERS et l'article qui renvoie sur la page des subtilités de compréhension et d'usage de ces différentes fonctions (lien d'ailleurs présent sur la page de chacune de ces fonctions et que je n'avais encore jamais vu), COVERS devrait faire l'affaire. Et effectivement, les premiers tests semblent concluants.
MERCI ENCORE....
Hors ligne
#4 Thu 20 February 2014 15:32
- Cornet Jérémie
- Participant assidu
- Lieu: Nouméa
- Date d'inscription: 6 Apr 2008
- Messages: 229
Re: Trigger avec st_contains et sommets différents
Je confirme ce que dit Vianney :
ST_CONTAINS c'est entièrement contenu (y compris pas de bordure commune donc de sommets communs) alors que ST_COVERS autorise la bordure.
Pour la différence "st_contains(geomA,geomB) et le couple geomA && geomB and _st_contains(geomA,geomB)", les 2 cas doivent être autant rapide (différence infime).
Toutes les fonctions st_ font le préfiltrage d'index alors que les anciennes fonctions (sans st_) ne le faisaient pas et que les _st_ ne le font pas non plus.
Dernière modification par Cornet Jérémie (Thu 20 February 2014 15:36)
Hors ligne
#5 Thu 20 February 2014 16:00
- Nicolas Ribot
- Membre
- Lieu: Toulouse
- Date d'inscription: 9 Sep 2005
- Messages: 1554
Re: Trigger avec st_contains et sommets différents
Bonjour,
Si le point de la nouvelle parcelle est exactement sur la frontiere de l'ancienne frontiere, st_contains doit renvoyer vrai:
Code:
select st_contains ( 'POLYGON((0 0, 3 0, 3 3, 0 3, 0 0))'::geometry, 'POLYGON((2 0, 2.5 2, 1.5 2, 2 0))'::geometry );
A la precision près, le point est peut etre considéré comme légèrement en dehors.
st_snapToGrid peut aider a résoudre le problème, surement:
J'ai fait un test en numerisant deux polygones, le deuxieme ayant un point sur la frontiere du premier (fonction snapping de mon outil):
Le test renvoie faux (contains(a, b))
Si je rajoute st_snapToGrid(geom, 0.00000001), le test renvoie vrai.
Nicolas
Hors ligne
#6 Thu 20 February 2014 16:04
- Nicolas Ribot
- Membre
- Lieu: Toulouse
- Date d'inscription: 9 Sep 2005
- Messages: 1554
Re: Trigger avec st_contains et sommets différents
Je confirme ce que dit Vianney :
ST_CONTAINS c'est entièrement contenu (y compris pas de bordure commune donc de sommets communs) alors que ST_COVERS autorise la bordure.
Non: la définition est:
Geometry A contains Geometry B if and only if no points of B lie in the exterior of A, and at least one point of the interior of B lies in the interior of A.
La géométrie contenue peut donc avoir des sommets communs avec la frontières de la geométrie contenante. Contains renvoie faux qd toute la frontiere du contenu est confondue avec celle du contenant.
D'ou le fait qu'un polygone ne contiennent pas sa frontiere, mais se contient lui-meme
Pour la différence "st_contains(geomA,geomB) et le couple geomA && geomB and _st_contains(geomA,geomB)", les 2 cas doivent être autant rapide (différence infime).
Toutes les fonctions st_ font le préfiltrage d'index alors que les anciennes fonctions (sans st_) ne le faisaient pas et que les _st_ ne le font pas non plus.
Hors ligne
#7 Fri 21 February 2014 10:25
- SANTANNA
- Moderateur
- Lieu: Angers
- Date d'inscription: 18 Jan 2008
- Messages: 3945
Re: Trigger avec st_contains et sommets différents
Bonjour à tous,
et merci pour ces échanges.
Nicolas, j'ignore si c'est du fait des balises mais je ne comprends pas et ne suis pas d'accord quand tu écris
Contains renvoie faux qd toute la frontiere du contenu est confondue avec celle du contenant.
D'ou le fait qu'un polygone ne contiennent pas sa frontiere, mais se contient lui-meme
Lorsque je duplique une entité, si le polygone se contient lui-même, ça doit pouvoir dire qu'il contient sa copie, alors même que toute la frontière de sa copie est confondue avec la sienne.
Et ça, ça n'a jamais posé de problème dans mon cas; le trigger st_contains retournait vrai et récupérait l'info. De même, lorsque la frontière du contenu était une partie de celle du contenant (sommets identiques sur la frontière).
Par contre, c'est quand
La géométrie contenue [a] des sommets communs avec la frontière de la geométrie contenante
et que ces sommets ne sont pas des sommets de la géométrie contenante que st_contains renvoyait faux. Ce que st_covers semble gérer.
Pour ceuqx que ça intéresse, ci-après le lien que j'évoquais plus haut et qui parle de ces subtilités http://lin-ear-th-inking.blogspot.fr/20 … atial.html
Hors ligne
#8 Fri 21 February 2014 12:04
- Nicolas Ribot
- Membre
- Lieu: Toulouse
- Date d'inscription: 9 Sep 2005
- Messages: 1554
Re: Trigger avec st_contains et sommets différents
Bonjour,
Oui, ma phrase sur la frontiere n'est pas claire et prete a confusion: qd je parle de frontière d'un polygone, il s'agit de la linestring qui compose la frontiere.
Je vais essayer d'etre un peu plus clair, avec des exemples simples (a noter que les définitions topologiques des fonctions définies par l'OGC sont parfois contre intuitives, mais décrivent toujours toutes les combinaisons entre intérieurs, exterieur et frontieres des objets:
Soit un polygone simple: POLYGON((0 0, 1 0, 1 1, 0 1, 0 0))
Il se contient lui-meme ?
Code:
nicolas=# select st_contains ( nicolas(# 'POLYGON((0 0, 1 0, 1 1, 0 1, 0 0))'::geometry, nicolas(# 'POLYGON((0 0, 1 0, 1 1, 0 1, 0 0))'::geometry nicolas(# ); st_contains ------------- t (1 ligne)
Contient-il sa frontiere ?
Code:
nicolas=# select st_contains ( nicolas(# 'POLYGON((0 0, 1 0, 1 1, 0 1, 0 0))'::geometry, nicolas(# st_boundary('POLYGON((0 0, 1 0, 1 1, 0 1, 0 0))'::geometry) nicolas(# ); st_contains ------------- f (1 ligne)
C'est en ce sens que ce n'est pas intuitif: un polygone peut contenir un autre polygone, mais ne contient pas une "partie" de ce polygone qui est sa frontiere.
St_contains renvoie également vrai si un ou plusieurs sommets du polygone contenu sont sur la frontière du polygone contenant:
Code:
select st_contains ( 'POLYGON((0 0, 3 0, 3 3, 0 3, 0 0))'::geometry, 'POLYGON((2 0, 2.5 2, 1.5 2, 2 0))'::geometry ); st_contains ------------- t (1 ligne)
Dans cet exemple, les deux polygones partagent des points sur leur frontiere et st_contains renvoie vrai.
st_covers, opérateur non défini par l'OGC, a ete introduit pour permettre de répondre vrai aux deux cas précédents: Il y a souvent des process géomatiques dans lesquels les objets sont décomposés en leurs frontière et intérieur, et st_contains pose donc des pb dans ces cas.
Code:
select st_covers ( 'POLYGON((0 0, 1 0, 1 1, 0 1, 0 0))'::geometry, 'POLYGON((0 0, 1 0, 1 1, 0 1, 0 0))'::geometry ); st_covers ----------- t (1 ligne) select st_covers ( 'POLYGON((0 0, 1 0, 1 1, 0 1, 0 0))'::geometry, st_boundary('POLYGON((0 0, 1 0, 1 1, 0 1, 0 0))'::geometry) ); st_covers ----------- t (1 ligne)
Dans votre cas de numérisation, le problème vient, a mon sens, de problèmes de précision: le point numérisé "sur la frontière" de l'autre polygone n'est pas sur la frontière, a la précision prés: contains renvoie faux.
Ces problèmes de précisions peuvent etre particulièrement piégeux (cf forum postgis users): il y a des cas ou st_distance(point, ligne) renvoie 0: le point est donc sur la ligne, mais st_contains ou st_within utilisé avec les memes géométries renverra faux. Postgis n'est pas robuste dans tous les cas, d'ou souvent le conseil de travailler en coordonnées entières ou d'utiliser st_snapToGrid pour eviter ces pb d'arrondis.
Si je reprends votre phrase:
Par contre, c'est quand La géométrie contenue [a] des sommets communs avec la frontière de la geométrie contenante
et que ces sommets ne sont pas des sommets de la géométrie contenante que st_contains renvoyait faux.
Non ! D'abord parce qu'il n'y a pas, dans Postgis, la notion de "ces sommets ne sont pas des sommets de la géométrie contenante": Pour postgis, il n'y a que des points communs entre géométries, que ce soit les memes points (des sommets), ou un sommet et un point sur un segment.
Le probleme vient du fait que quand postgis calcule la coordonnée commune entre deux objets, il peut y avoir des erreurs d'arrondis qui font que le résultat de la fonction est faux.
Ce que st_covers semble gérer.
Pas plus que st_contains: pour des polygones, st_contains et st_covers renverront tjs le meme résultat... aux erreurs d'arrondi près: les algorithmes étant différents, une des deux fonctions peut "planter" alors que l'autre marchera, pour les meme geometries.
Pour résumer, dans votre cas de numérisation:
Vous travaillez avec des polygones et vous voulez controler que le nouveau polygone est bien contenu dans le polygone d'origine: st_contains et st_covers renverront vrai.
Vous trouvez des résultats curieux avec l'une ou l'autre des fonctions: il faut controler la précision des données.
Et enfin, les algorithmes étant différents entre les deux fonctions, une est peut etre plus "robuste" que l'autre et sera moins sensible aux erreurs d'arrondis, mais il ne faut pas compter sur ce comportement dans votre application.
Nicolas
Hors ligne
#9 Mon 24 February 2014 11:39
- SANTANNA
- Moderateur
- Lieu: Angers
- Date d'inscription: 18 Jan 2008
- Messages: 3945
Re: Trigger avec st_contains et sommets différents
Bonjour,
Merci pour toutes ces précisions, Nicolas.
Je vais voir comment intégrer st_snapToGrid dans ma fonction de récupération pour consolider un peu tout ceci. A première vue, je vais surement revenir poser des questions sur cette fonction mais bon... je vais chercher de mon côté avant.
PS: Si ce n'est pas indiscret, comment fait-on pour connaître autant de choses que toi sur le fonctionnement de PostgreSQL/PostGIS ?
Hors ligne
#10 Mon 24 February 2014 12:38
- Nicolas Ribot
- Membre
- Lieu: Toulouse
- Date d'inscription: 9 Sep 2005
- Messages: 1554
Re: Trigger avec st_contains et sommets différents
Bonjour,
Avec OpenJump, j'ai observé le meme probleme que vous: un polygone de départ (pg1), je numérise un polygone a l'intérieur (pg2), avec des sommets qui se trouvent sur le bord de pg1 grâce à l'option de snapping des points:
st_contains(pg1, pg2) renvoie faux.
Avec st_contains(pg1, st_snapToGrid(pg2, 0.00000001)) j'obtiens vrai.
Un petit arrondi sur les coordonnées pour éviter le problème de précision des calculs.
PS: no life...
Hors ligne