Prendre un thé
Tapoter des doights
Monsieur fantôme
Quelle heure est-il ?
BazinGa's Tips & tuto IT
BazinGa's - Tips & tuto IT

ST_Touches ou ST_Touches pas ? Passons la frontière

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

Sommet sur la frontière d’un point (en orange)
Pas de frontière
Sommets sur la frontière d’une ligne (en orange)
Seuls les sommets des extrémités
Sommets sur la frontière d’un polygone (en orange)
Tous les sommets

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 :

renvoyer « vrai » si et seulement si les seuls points en commun entre les deux géométries comparées appartiennent à l’union des frontières de ces deux géométries.

Documentation PostGIS

Plus simplement :

Renvoyer « vrai » si et seulement si l’intersection des deux géométries est un point ou une ligne (pas de surface commune) ET qu’au moins un point composant cette intersection est situé sur la frontière de l’une des deux géométries.

Arthur Bazin

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.

Point – Point
P-P
Point – Ligne (extrémité)
P-L(e)
Point – Ligne (sommet interne)
P-L(i)
Point – Polygone (contour externe)
P-S(e)
Point – Polygone (contour interne)
P-S(i)
Ligne (extrémité) – Ligne (extrémité)
L(e)-L(e)
Ligne (extrémité) – Ligne (sommet interne)
L(e)-L(i)
Ligne (sommet interne) – Ligne (sommet interne)
L(i)-L(i)
Ligne (extrémité) – Polygone (contour externe)
L(e)-S(e)
Ligne (extrémité) – Polygone (contour interne)
L(e)-S(i)
Ligne (sommet interne) – Polygone (contour externe)
L(i)-S(e)
Ligne (sommet interne) – Polygone (contour interne)
L(i)-S(i)
Polygone (contour externe) – Polygone (contour externe)
S(e)-S(e)
Polygone (contour interne) – Polygone (contour externe)
S(i)-S(e)
Polygone (contour externe) – Polygone (contour externe) – Chevauchement interne
S(e)-S(e)+ci

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

50%