Introduction
Il m’est arrivé d’avoir besoin de comparer plusieurs objets géométriques pour déterminer s’ils se superposaient ou pas. Un simple ST_Intersects
est intéressant mais il est parfois nécessaire de chercher au delà de la simple superposition.
C’est là qu’entrent en jeux les autres fonctions de relation topologiques. En effet, dans mon cas précis, j’avais besoin de savoir si une série de points intersectait une ligne uniquement par l’un de ses sommets. Je me suis naturellement tourné vers la fonction ST_Touches
pour savoir si les géométries se touchaient mais les résultats me parurent étranges : un point positionné sur un sommet situé au milieu de ma ligne ne la touchait pas…
J’ai donc creusé le sujet et me suis rendu compte que les résultats retournés par la fonction ST_Touches
n’étaient pas forcément ceux attendus (par moi en tout cas)… Il me fallait en fait mieux comprendre le fonctionnement de cette fonction.
La suite de cet article ne traite que du cas d’intersection d’objet par leurs sommets, pas des autres cas d’intersection (détectables avec ST_Intersects
).
Parlons frontière
Concentrons nous d’abord sur nos géométries et parlons de leurs frontières.
La frontière d’une géométrie représente une limite qui sépare l’extérieur de l’intérieur de celle-ci. On ne parlera donc pas de la même chose selon le type de géométrie rencontrée.
Dans le cas d’un point, il n’y a pas de frontière. C’est logique car un point représente une singularité, il n’y a ainsi pas d’intérieur et donc pas de frontière.
Dans le cas d’une ligne, la frontière est représentée par les sommets situés sur les deux extrémités de la ligne. Dans ce cas, comme il n’y a pas de surface interne, on considère que tous les sommets autres que les sommets des extrémités sont à l’intérieur. Il y a donc deux frontières ponctuelles situées à chaque extrémité de la ligne.
Dans le cas d’un polygone, l’intérieur est facilement distinguable. Ainsi, la frontière est représentée par l’ensemble des points qui composent le polygone, y compris les points composant les trous du polygone (l’intérieur d’un trou étant considéré comme l’extérieur d’un polygone).
Le principe est exactement le même pour les multi-géométries.
Voici les trois types de géométries avec leurs frontières respectives (en orange) :
ST_Touches
Définition
Étrange fonctionnement que celui de la fonction ST_Touches
. En effet, une définition simpliste serait : renvoyer « vrai » si et seulement si deux objets se touchent. Simple et efficace mais la définition est en fait bien plus subtile.
En effet, si l’on se réfère à la documentation officielle voici la définition exacte :
Plus simplement :
On comprends donc que les géométries doivent se toucher (au sens commun de « toucher ») sans s’intersecter. Mais également que les points en communs entre ces deux géométries soient situés sur au moins une frontière. Et c’est là que tout bascule…
En effet, comme nous l’avons vu plus haut, la frontière d’une géométrie est un concept spécifique. Ainsi, on peut en déduire qu’un point ne peut pas toucher un autre point, ou bien qu’une ligne ne peut toucher un point que par son extrémité de même que deux lignes ne peuvent pas se toucher si leurs sommets communs sont situés au milieu de celles-ci.
Exemple
Pour mieux comprendre le fonctionnement de cette fonction, voici une liste de géométries qui s’intersectent et sur lesquelles nous allons tester quelques requêtes.
Voici une requête utilisant les géométries précédentes avec la fonction ST_Touches
ainsi que les résultats associés :
SELECT "code", "description", --"geom_1", --"geom_2", ST_Intersection("geom_1", "geom_2") AS "geometry_commune", ST_Touches("geom_1", "geom_2") AS "st_touches" FROM ( VALUES ('P-P', 'Point - Point', 'POINT(0 0)'::geometry, 'POINT(0 0)'::geometry), ('P-L(e)', 'Point - Ligne (extrémité)', 'POINT(0 0)'::geometry, 'LINESTRING (0 0, 1 0)'::geometry), ('P-L(i)', 'Point - Ligne (sommet interne)', 'POINT(1 0)'::geometry, 'LINESTRING (0 0, 1 0, 2 0)'::geometry), ('P-S(e)', 'Point - Polygone (contour externe)', 'POINT(0 0)'::geometry, 'POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))'::geometry), ('P-S(i)', 'Point - Polygone (contour interne)', 'POINT(1 1)'::geometry, 'POLYGON ((0 0, 3 0, 3 3, 0 3, 0 0), (1 1, 2 1, 2 2, 1 2, 1 1))'::geometry), ('L(e)-L(e)', 'Ligne (extrémité) - Ligne (extrémité)', 'LINESTRING (-1 0, 0 0)'::geometry, 'LINESTRING (0 0, 1 0, 2 0)'::geometry), ('L(e)-L(i)', 'Ligne (extrémité) - Ligne (intérieur)', 'LINESTRING (1 1, 1 0)'::geometry, 'LINESTRING (0 0, 1 0, 2 0)'::geometry), ('L(i)-L(i)', 'Ligne (intérieur) - Ligne (intérieur)', 'LINESTRING (1 1, 1 0, 2 1)'::geometry, 'LINESTRING (0 0, 1 0, 2 0)'::geometry), ('L(e)-S(e)', 'Ligne (extrémité) - Polygone (contour externe)', 'LINESTRING (-1 0, 0 0)'::geometry, 'POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))'::geometry), ('L(e)-S(i)', 'Ligne (extrémité) - Polygone (contour interne)', 'LINESTRING (1 1, 1.5 1.5)'::geometry, 'POLYGON ((0 0, 3 0, 3 3, 0 3, 0 0), (1 1, 2 1, 2 2, 1 2, 1 1))'::geometry), ('L(i)-S(e)', 'Ligne (intérieur) - Polygone (contour externe)', 'LINESTRING (-1 0, 0 0, 0 -1)'::geometry, 'POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))'::geometry), ('L(i)-S(i)', 'Ligne (intérieur) - Polygone (contour interne)', 'LINESTRING (1.2 1.5, 1 1, 1.5 1.2)'::geometry, 'POLYGON ((0 0, 3 0, 3 3, 0 3, 0 0), (1 1, 2 1, 2 2, 1 2, 1 1))'::geometry), ('S(e)-S(e)', 'Polygone (contour externe) - Polygone (contour externe)', 'POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))'::geometry, 'POLYGON ((1 1, 2 1, 2 2, 1 2, 1 1))'::geometry), ('S(e)-S(i)', 'Polygone (contour externe) - Polygone (contour interne)', 'POLYGON ((1 1, 3 2, 3 3, 2 3, 1 1))'::geometry, 'POLYGON ((0 0, 5 0, 5 5, 0 5, 0 0), (1 1, 4 1, 4 4, 1 4, 1 1))'::geometry), ('S(e)-S(e)+ci', 'Polygone (contour externe) - Polygone (contour externe) - Chevauchement interne', 'POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))'::geometry, 'POLYGON ((0 0, 2 0, 2 2, 0 2, 0 0))'::geometry) ) AS t1 ("code", "description", "geometry_commune", "geom_1", "geom_2")
code |description |geometry_commune |st_touches| ------------|-------------------------------------------------------------------------------|-----------------------------------|----------| P-P |Point - Point |POINT (0 0) |false | P-L(e) |Point - Ligne (extrémité) |POINT (0 0) |true | P-L(i) |Point - Ligne (sommet interne) |POINT (1 0) |false | P-S(e) |Point - Polygone (contour externe) |POINT (0 0) |true | P-S(i) |Point - Polygone (contour interne) |POINT (1 1) |true | L(e)-L(e) |Ligne (extrémité) - Ligne (extrémité) |POINT (0 0) |true | L(e)-L(i) |Ligne (extrémité) - Ligne (intérieur) |POINT (1 0) |true | L(i)-L(i) |Ligne (intérieur) - Ligne (intérieur) |POINT (1 0) |false | L(e)-S(e) |Ligne (extrémité) - Polygone (contour externe) |POINT (0 0) |true | L(e)-S(i) |Ligne (extrémité) - Polygone (contour interne) |POINT (1 1) |true | L(i)-S(e) |Ligne (intérieur) - Polygone (contour externe) |POINT (0 0) |true | L(i)-S(i) |Ligne (intérieur) - Polygone (contour interne) |POINT (1 1) |true | S(e)-S(e) |Polygone (contour externe) - Polygone (contour externe) |POINT (1 1) |true | S(e)-S(i) |Polygone (contour externe) - Polygone (contour interne) |POINT (1 1) |true | S(e)-S(e)+ci|Polygone (contour externe) - Polygone (contour externe) - Chevauchement interne|POLYGON ((0 1, 1 1, 1 0, 0 0, 0 1))|false |
Intéressant, n’est-ce pas ?
Aller plus loin que ST_Touches
Comme nous l’avons vu, la fonction ST_Touches
implique des résultats bien particuliers. Voici comment aller plus loin.
Sommets en commun
Pour détecter si deux géométries sont en contact par leurs sommets, l’astuce consiste en la décomposition des géométries en leurs sommets respectifs puis de vérifier l’intersection entre ces sommets.
SELECT "code", "description", --"geom_1", --"geom_2", ST_Intersection("geom_1", "geom_2") AS "geometry_commune", ST_Intersects(ST_Points("geom_1"), ST_Points("geom_2")) AS "st_intersects_sommets" FROM ( VALUES ('P-P', 'Point - Point', 'POINT(0 0)'::geometry, 'POINT(0 0)'::geometry), ('P-L(e)', 'Point - Ligne (extrémité)', 'POINT(0 0)'::geometry, 'LINESTRING (0 0, 1 0)'::geometry), ('P-L(i)', 'Point - Ligne (sommet interne)', 'POINT(1 0)'::geometry, 'LINESTRING (0 0, 1 0, 2 0)'::geometry), ('P-S(e)', 'Point - Polygone (contour externe)', 'POINT(0 0)'::geometry, 'POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))'::geometry), ('P-S(i)', 'Point - Polygone (contour interne)', 'POINT(1 1)'::geometry, 'POLYGON ((0 0, 3 0, 3 3, 0 3, 0 0), (1 1, 2 1, 2 2, 1 2, 1 1))'::geometry), ('L(e)-L(e)', 'Ligne (extrémité) - Ligne (extrémité)', 'LINESTRING (-1 0, 0 0)'::geometry, 'LINESTRING (0 0, 1 0, 2 0)'::geometry), ('L(e)-L(i)', 'Ligne (extrémité) - Ligne (intérieur)', 'LINESTRING (1 1, 1 0)'::geometry, 'LINESTRING (0 0, 1 0, 2 0)'::geometry), ('L(i)-L(i)', 'Ligne (intérieur) - Ligne (intérieur)', 'LINESTRING (1 1, 1 0, 2 1)'::geometry, 'LINESTRING (0 0, 1 0, 2 0)'::geometry), ('L(e)-S(e)', 'Ligne (extrémité) - Polygone (contour externe)', 'LINESTRING (-1 0, 0 0)'::geometry, 'POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))'::geometry), ('L(e)-S(i)', 'Ligne (extrémité) - Polygone (contour interne)', 'LINESTRING (1 1, 1.5 1.5)'::geometry, 'POLYGON ((0 0, 3 0, 3 3, 0 3, 0 0), (1 1, 2 1, 2 2, 1 2, 1 1))'::geometry), ('L(i)-S(e)', 'Ligne (intérieur) - Polygone (contour externe)', 'LINESTRING (-1 0, 0 0, 0 -1)'::geometry, 'POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))'::geometry), ('L(i)-S(i)', 'Ligne (intérieur) - Polygone (contour interne)', 'LINESTRING (1.2 1.5, 1 1, 1.5 1.2)'::geometry, 'POLYGON ((0 0, 3 0, 3 3, 0 3, 0 0), (1 1, 2 1, 2 2, 1 2, 1 1))'::geometry), ('S(e)-S(e)', 'Polygone (contour externe) - Polygone (contour externe)', 'POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))'::geometry, 'POLYGON ((1 1, 2 1, 2 2, 1 2, 1 1))'::geometry), ('S(e)-S(i)', 'Polygone (contour externe) - Polygone (contour interne)', 'POLYGON ((1 1, 3 2, 3 3, 2 3, 1 1))'::geometry, 'POLYGON ((0 0, 5 0, 5 5, 0 5, 0 0), (1 1, 4 1, 4 4, 1 4, 1 1))'::geometry), ('S(e)-S(e)+ci', 'Polygone (contour externe) - Polygone (contour externe) - Chevauchement interne', 'POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))'::geometry, 'POLYGON ((0 0, 2 0, 2 2, 0 2, 0 0))'::geometry) ) AS t1 ("code", "description", "geom_1", "geom_2")
code |description |geometry_commune |st_intersects_sommets| ------------|-------------------------------------------------------------------------------|-----------------------------------|---------------------| P-P |Point - Point |POINT (0 0) |true | P-L(e) |Point - Ligne (extrémité) |POINT (0 0) |true | P-L(i) |Point - Ligne (sommet interne) |POINT (1 0) |true | P-S(e) |Point - Polygone (contour externe) |POINT (0 0) |true | P-S(i) |Point - Polygone (contour interne) |POINT (1 1) |true | L(e)-L(e) |Ligne (extrémité) - Ligne (extrémité) |POINT (0 0) |true | L(e)-L(i) |Ligne (extrémité) - Ligne (intérieur) |POINT (1 0) |true | L(i)-L(i) |Ligne (intérieur) - Ligne (intérieur) |POINT (1 0) |true | L(e)-S(e) |Ligne (extrémité) - Polygone (contour externe) |POINT (0 0) |true | L(e)-S(i) |Ligne (extrémité) - Polygone (contour interne) |POINT (1 1) |true | L(i)-S(e) |Ligne (intérieur) - Polygone (contour externe) |POINT (0 0) |true | L(i)-S(i) |Ligne (intérieur) - Polygone (contour interne) |POINT (1 1) |true | S(e)-S(e) |Polygone (contour externe) - Polygone (contour externe) |POINT (1 1) |true | S(e)-S(i) |Polygone (contour externe) - Polygone (contour interne) |POINT (1 1) |true | S(e)-S(e)+ci|Polygone (contour externe) - Polygone (contour externe) - Chevauchement interne|POLYGON ((0 1, 1 1, 1 0, 0 0, 0 1))|true |
Cette précédente requête en intéressante car elle permet de remonter tous les objets qui possèdent des sommets en commun. En revanche, elle récupère aussi les objets qui s’intersectent en plus d’avoir des sommets en commun (par exemple deux polygones qui se superposent).
Sommets en commun sans intersection de surface
Voici une dernière requête qui permet d’identifier les objets qui possèdent des sommets en communs sans pour autant avoir de surface commune, elle vérifie que les objets possèdent des points en commun et que le résultat de leur intersection soit un point ou une ligne :
SELECT "code", "description", --"geom_1", --"geom_2", ST_Intersection("geom_1", "geom_2") AS "geometry_commune", ST_Intersects(ST_Points("geom_1"), ST_Points("geom_2")) AND (ST_GeometryType(ST_Intersection("geom_1", "geom_2")) IN ('ST_Point', 'ST_Line')) AS "requete_personnalise" FROM ( VALUES ('P-P', 'Point - Point', 'POINT(0 0)'::geometry, 'POINT(0 0)'::geometry), ('P-L(e)', 'Point - Ligne (extrémité)', 'POINT(0 0)'::geometry, 'LINESTRING (0 0, 1 0)'::geometry), ('P-L(i)', 'Point - Ligne (sommet interne)', 'POINT(1 0)'::geometry, 'LINESTRING (0 0, 1 0, 2 0)'::geometry), ('P-S(e)', 'Point - Polygone (contour externe)', 'POINT(0 0)'::geometry, 'POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))'::geometry), ('P-S(i)', 'Point - Polygone (contour interne)', 'POINT(1 1)'::geometry, 'POLYGON ((0 0, 3 0, 3 3, 0 3, 0 0), (1 1, 2 1, 2 2, 1 2, 1 1))'::geometry), ('L(e)-L(e)', 'Ligne (extrémité) - Ligne (extrémité)', 'LINESTRING (-1 0, 0 0)'::geometry, 'LINESTRING (0 0, 1 0, 2 0)'::geometry), ('L(e)-L(i)', 'Ligne (extrémité) - Ligne (intérieur)', 'LINESTRING (1 1, 1 0)'::geometry, 'LINESTRING (0 0, 1 0, 2 0)'::geometry), ('L(i)-L(i)', 'Ligne (intérieur) - Ligne (intérieur)', 'LINESTRING (1 1, 1 0, 2 1)'::geometry, 'LINESTRING (0 0, 1 0, 2 0)'::geometry), ('L(e)-S(e)', 'Ligne (extrémité) - Polygone (contour externe)', 'LINESTRING (-1 0, 0 0)'::geometry, 'POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))'::geometry), ('L(e)-S(i)', 'Ligne (extrémité) - Polygone (contour interne)', 'LINESTRING (1 1, 1.5 1.5)'::geometry, 'POLYGON ((0 0, 3 0, 3 3, 0 3, 0 0), (1 1, 2 1, 2 2, 1 2, 1 1))'::geometry), ('L(i)-S(e)', 'Ligne (intérieur) - Polygone (contour externe)', 'LINESTRING (-1 0, 0 0, 0 -1)'::geometry, 'POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))'::geometry), ('L(i)-S(i)', 'Ligne (intérieur) - Polygone (contour interne)', 'LINESTRING (1.2 1.5, 1 1, 1.5 1.2)'::geometry, 'POLYGON ((0 0, 3 0, 3 3, 0 3, 0 0), (1 1, 2 1, 2 2, 1 2, 1 1))'::geometry), ('S(e)-S(e)', 'Polygone (contour externe) - Polygone (contour externe)', 'POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))'::geometry, 'POLYGON ((1 1, 2 1, 2 2, 1 2, 1 1))'::geometry), ('S(e)-S(i)', 'Polygone (contour externe) - Polygone (contour interne)', 'POLYGON ((1 1, 3 2, 3 3, 2 3, 1 1))'::geometry, 'POLYGON ((0 0, 5 0, 5 5, 0 5, 0 0), (1 1, 4 1, 4 4, 1 4, 1 1))'::geometry), ('S(e)-S(e)+ci', 'Polygone (contour externe) - Polygone (contour externe) - Chevauchement interne', 'POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))'::geometry, 'POLYGON ((0 0, 2 0, 2 2, 0 2, 0 0))'::geometry) ) AS t1 ("code", "description", "geom_1", "geom_2")
code |description |geometry_commune |requete_personnalise| ------------|-------------------------------------------------------------------------------|-----------------------------------|--------------------| P-P |Point - Point |POINT (0 0) |true | P-L(e) |Point - Ligne (extrémité) |POINT (0 0) |true | P-L(i) |Point - Ligne (sommet interne) |POINT (1 0) |true | P-S(e) |Point - Polygone (contour externe) |POINT (0 0) |true | P-S(i) |Point - Polygone (contour interne) |POINT (1 1) |true | L(e)-L(e) |Ligne (extrémité) - Ligne (extrémité) |POINT (0 0) |true | L(e)-L(i) |Ligne (extrémité) - Ligne (intérieur) |POINT (1 0) |true | L(i)-L(i) |Ligne (intérieur) - Ligne (intérieur) |POINT (1 0) |true | L(e)-S(e) |Ligne (extrémité) - Polygone (contour externe) |POINT (0 0) |true | L(e)-S(i) |Ligne (extrémité) - Polygone (contour interne) |POINT (1 1) |true | L(i)-S(e) |Ligne (intérieur) - Polygone (contour externe) |POINT (0 0) |true | L(i)-S(i) |Ligne (intérieur) - Polygone (contour interne) |POINT (1 1) |true | S(e)-S(e) |Polygone (contour externe) - Polygone (contour externe) |POINT (1 1) |true | S(e)-S(i) |Polygone (contour externe) - Polygone (contour interne) |POINT (1 1) |true | S(e)-S(e)+ci|Polygone (contour externe) - Polygone (contour externe) - Chevauchement interne|POLYGON ((0 1, 1 1, 1 0, 0 0, 0 1))|false |
Vous pouvez ainsi voir que selon vos besoins, il vous faudra bien étudier les résultats retournés par une fonction qui semble simple car son fonctionnement peut être plus subtile qu’il n’y parait.
Cet article vous a plu ?
N'hésitez pas à le partager, il interessera surement certains de vos contacts.
Les thèmes suivants contiennent des articles en lien avec celui-ci, allez faire un tour :
BDDPostGISPostgreSQLSIG frontièreintersectionsommetst_touches