Après les deux article introductifs,L’analyse spatiale avec SQL:1-Introduction, 2-les fausses idées reçues, voici, maintenant, la dizaine de fonctions de relation spatiale de Postgis (mais que vous trouverez aussi avec Spatialite). Comme dit précédemment, ce n’est qu’une toute petite partie des fonctions disponibles, mais c’est vraiment le noyau indispensable pour démarre l’analyse spatiale avec SQL.
Les types ou dimensions des géométries
Avant de voir les différentes fonctions il faut rappeler sur quoi nous allons travailler. Les différentes fonctions ont comme paramètres deux géométries. Mais les géométries ne sont pas toutes de même type (on parle aussi de dimensions): nous avons trois types majeurs (point, ligne, surface) et trois type dérivés (multipoints, multilignes, multisurfaces). La distinction est importante car les fonctions fonctionnent sur la détermination, pour chaque entité (géométrie) de ce que l’on considère l’intérieur de l’entité et de ce que l’on considère l’extérieur. Pour une couche de polygones vous voyez tout de suite de quoi on parle et vous n’aurez aucun mal à distinguer l’intérieur des polygones de leur extérieur.
Pour les points, ça commence à se corser car l’intérieur d’un point est un peu plus difficile à concevoir. Et ça devient presque métaphysique quand on parle de l’intérieur d’une entité multipoint.
Cette image peut représenter trois entités ponctuelles, dans le cas d’une couche de points, ou bien une seule entité constituée par trois points, dans le cas d’une couche multipoints. Mais dans les deux cas, l’intérieur est constitué par le ou les points seulement.
Par exemple, dans la figure suivante, est-ce que la ligne traverse la géométrie de la couche multipoint?
La réponse est NON. Pour traverse une géométrie, il faut qu’il y ait une partie commune « interne » aux deux géométries.
Dans la figure suivante, par contre, la réponse est OUI, la ligne traverse l’entité multipoint.
Vous aurez compris que l’intérieur d’une ligne est la ligne elle-même, comme pour les points.
On peut séparer les fonctions de relation spatiale en deux grands groupes: celles qui s’appliquent à tous les types de géométrie et ceux qui ne s’appliquent qu’à certains types.
Pour une description détaillée des fonctions, reportez vous à la documentation Postgis: https://postgis.net/docs/manual-1.5/reference.html#Spatial_Relationships_Measurements
Les fonctions applicables à tous les types de géométrie
Les fonction d’intersection, de différence, de contenance ou de contenu et la fonction toucher acceptent dans les deux paramètres de la fonction n’importe quel type de géométrie de base ou multiple. On peut donc calculer l’intersection d’une couche multipoint avec un couche polyligne, de polygones et de multipolygones, etc.
st_Intersect
C’est la fonction la plus générique. Elle renvoie VRAI si l’intérieur d’une géométrie occupe la même place qu’un autre point intérieur de la deuxième géométrie, en d’autres termes si elles ont au moins un point de l’espace en commun. Si les autres fonctions telles que st_Crosses, st_Overlaps, st_touches, st_Within ou st_Contains retournent une valeur VRAI sur des géométries, st_Intersect retournera aussi une valeur VRAI.
L’exemple suivant montre le résultat de la requête permettant de trouver les parcelles qui sont affectées par une zone inondable de risque maximal:
SELECT parcelles.*
FROM parcelles,carto_risque
WHERE ST_Intersects(parcelles.geometry,carto_risque.geometry)
AND carto_risque.zone = ‘max’
Toute parcelle qui partage ne serait-ce qu’un point dans l’espace avec la zone inondable est sélectionnée.
st_Disjoint
Est la fonction inverse de st_intersect. Elle renvoie VRAI si l’intérieur d’une géométrie n’occupe pas la même place qu’un autre point intérieur de la deuxième géométrie, en d’autres termes si elles n’ont aucun point de l’espace en commun.
On obtient le même résultat qu’avec la commande st_intersects avec la requête:
SELECT parcelles.*
FROM parcelles,carto_risque
WHERE ST_Disjoint(parcelles.geometry,carto_risque.geometry)=false
AND carto_risque.zone = ‘max’
C’est un peu tiré par les cheveux de faire une requête inversée, mais si vous cherchez vraiment les géométries disjointes, il vaut mieux utiliser st_Intersects =false, car une requête utilisant st_intersect s’effectue avec les index spatiaux tandis que st_disjoint se fait en parcourant séquentiellement toutes les entités. les temps de réponse seront nettement différents!
st_Contains et st_Within
Ces deux fonctions testent si une géométrie est totalement à l’intérieur d’une autre. st_Within(geomA,geomB) retourne VRAI si la première géométrie (geomA) est complètement contenue dans l’autre (geomB).
st_Contains(geomA,geomB) retourne VRAI si la deuxième géométrie (geomB) est complètement contenue dans la première (geomA).
L’exemple suivant montre le résultat de la requête permettant de trouver les parcelles totalement incluses dans une zone inondable de risque maximal:
SELECT parcelles.*
FROM parcelles, carto_risque
WHERE ST_Within(parcelles.geometry, carto_risque.geometry)
AND carto_risque.zone = ‘max’
Seules les parcelles dont tous les points sont à l’intérieur de la zone inondable sont sélectionnées.
On obtient exactement le même résultat avec la requête
SELECT parcelles.*
FROM parcelles, carto_risque
WHERE ST_Contains(carto_risque.geometry, parcelles.geometry)
AND carto_risque.zone = ‘max’
st_Touches
Nous avons vu un exemple de cette fonction dans l’article précédent. Cette fonction retourne VRAI si les deux géométries ont au moins un point en commun mais que leurs intérieurs ne s’intersectent pas. En d’autres termes seuls les contours ont un ou plusieurs points en commun.
Les fonctions applicables selon le type de géométrie
st_Equals
St_Equals ne fonctionne que sur des géométries de même type. Il renvoie VRAI si les deux géométries partagent tous les points de l’espace, c’est à dire si toutes les coordonnées XY de la première géométrie sont identiques aux coordonnées de la deuxième. L’ordre des entités dans la couche n’intervient pas du tout dans la comparaison.
st_Crosses
St_Crosses ne s’applique pas à toutes les combinaisons de géométries. Elle s’applique aux combinaisons suivantes:
Les couches de points sont exclues. La fonction retourne VRAI si le résultat de l’intersection se retrouve à l’intérieur des deux géométries
Si on reprend notre exemple précédent, la figure suivante montre le résultat de la requête permettant de trouver les parcelles qui sont partiellement affectées par une zone inondable de risque maximal:
SELECT parcelles.*
FROM parcelles,carto_risque
WHERE ST_Overlaps(parcelles.geometry,carto_risque.geometry)
AND carto_risque.zone = ‘max’
Seules les parcelles qui ont des points à l’intérieur ET à l’extérieur de la zone inondable sont sélectionnées.
St_Distance
Contrairement aux autres fonctions, cette fonction ne renvoie pas VRAI ou FAUX mais la valeur de la plus petite distance calculée entre les deux géométries.
Si une partie (ou toute) de la géométrie de A se retrouve à l’intérieur de la géométrie B, la distance calculée sera 0.
L’exemple suivant montre le résultat de la requête permettant de trouver les parcelles situées à moins de 200 mètres d’une zone inondable de risque maximal:
SELECT parcelles.*
FROM parcelles,carto_risque
WHERE ST_Distance(parcelles.geometry,carto_risque.geometry) < 200
AND carto_risque.zone = ‘max’
Mais cette fonction est surtout utilisée pour calculer les distances entre géométries, car elle n’utilise pas les index spatiaux et fait un traitement séquentiel qui prend beaucoup plus de temps. Pour obtenir le même résultat que dans cette figure on utilise la fonction St_DWithin.
st_DWithin
La fonction prend la forme St_DWithin( geomA, geomB, Distance). Elle renvoie la valeur VRAI si la plus petite distance entre geomA et geomB est inférieure ou égale à Distance.
Pour l’exemple précédent la requête prend la forme:
SELECT parcelles.*
FROM parcelles,carto_risque
WHERE ST_Distance(parcelles.geometry,carto_risque.geometry,200)
AND carto_risque.zone = ‘max’
Et on obtient le même résultat cartographique.
Cette fonction est très utile pour répondre à une question telle que « Combien de parcelles se situent dans un buffer de 200 mètres autour de ce cours d’eau? ou de cette route? », sans avoir à calculer le buffer. On teste tout simplement la distance entre les géométries.
st_Covers et st_CoveredBy
J’ai laissé pour la fin ces deux fonctions qui sont presque équivalentes à st_Contains et st_Within. Le résultat sera presque toujours le même sauf dans certains cas très particuliers. C’est un peu tordu, mais il faut revenir à la notion de début de cet article, celle d’intérieur et extérieur d’une géométrie.
Quand nous avons un polygone, l’intérieur c’est l’espace contenu par les bords du polygone. Mais cette ligne imaginaire qui fait office de contour ne fait pas partie de l’intérieur, ni de l’extérieur non plus. C’est une limite.
Quand nous avons une ligne, même si elle n’a pas de dimension « épaisseur » on considère que la ligne est la zone « intérieure » de la géométrie.
Si nous avons par exemple une limite administrative de département, et cette limite correspond à une route qui est contenue dans une couche de polylignes, la fonction st_contains(polygone,ligne) va retourner FAUX car la zone intérieure de la ligne ne sera pas à l’intérieur du polygone (elle sera sur le contour).
La fonction st_Covers agit différemment. Elle trouvera que la limite du polygone « recouvre » le tronçon de route. La fonction st_CoveredBy cherche à savoir si la géométrie A et recouverte par la géométrie B et fonctionne de la même manière que st_Covers.
Bonjour,
Merci pour cette présentation !
En m’exerçant, je bute sur un problème : je souhaite répertorier les points présents sur une ligne.
Ma requête :
SELECT idPoint,idLigne
FROM Points, Lignes
WHERE st_intersects(Points.geometry,Lignes.geometry)
En résultat, je ne dispose que des points figurants sur les sommets des lignes/polylignes.
Les variantes within, touches etc… ne produisent également de résultats que si les points sont sur les sommets des lignes ou polylignes.
Comment puis-je connaître l’ensemble des points touchants la ligne, y compris ceux situés sur le segment entre chaque sommet ?
La requête qui marche pour votre cas est:
SELECT lespoints.id,leslignes.id
FROM lespoints, leslignes
WHERE st_dwithin(lespoints.geom,leslignes.geom,0)
Par contre ce n’est pas normal que st_intersects ne fonctionne pas avec les points situés sur la ligne. J’ai testé et c’est bien le cas. Je cherche, je creuse…
Merci. Je travaille avec Qgis, la fonction Dwithin n’est pas reconnue. J’observe que dans votre exemple vous utilisez st_distance avec une syntaxe différente.
En ce qui me concerne, avec ceci :
WHERE st_distance(points.geometry,lignes.geometry)=0
Je parviens à un résultat, mais qui ne prend pas en compte toutes les points sur les lignes.
Saisi d’un doute, j’ai modifié les points en les bougeant sur la lignes avec l’accrochage activé, et j’ai eu des résultats différents.
Au final, en mettant une valeur positive faible (0.001) pour faire une sorte de mini buffer, je parviens à récupérer tous mes points dans la requête.
WHERE st_distance(points.geometry,lignes.geometry)<0.001
C'est du bricolage mais ça marche… Un défaut dans l'accrochage de Qgis ?
Cela expliquerait le non fonctionnement de st_intersects.
Bonjour a tous,
Est il possible d’effectuer ces requêtes spatiales dans l’onglet Filtre des propriétés d’une couche?
Je ne trouve pas la bonne syntaxe pour effectuer un filtre avec un ST-intersect entre 2 couche de la base de donnée PostGIS
Merci d’avance
Cordialement
« l’onglet Filtre des propriétés d’une couche » me pose problème. Il n’y a pas d’onglet Filtre dans la fenêtre de propriétés. Il y a l’option « Filtrer… » dans le menu contextuel des couches.
Mais la réponse est déjà dans la question. Que ce soit dans les propriétés ou dans le menu contextuel, il s’agit bien d’opérations qui se font sur UNE couche et non plusieurs.Pour travailler sur plusieurs couches simultanément vous devez utiliser des outils tels que le gestionnaire BD ou les scripts de traitement.
Merci de votre réponse, je parlai bien de l’option « Filtrer» dans le menu contextuel des couches .
Du coup, si je comprend bien, il est impossible d’appliquer la requête intersect( entre 2 couche) a cette endroit.
Comment appliquer a toutes les couches d’un projet QGIS un filtre identique.
Par exemple, je souhaiterai que seule les entités de toutes mes couches qui intersecte un département ne s’affichent.
Avec la possibilité de changer de département et que le filtre s’applique automatiquement ?
Merci beaucoup pour votre réponse précèdent et merci d’avance pour la suivante
La solution consiste à créer une couche virtuelle (voir la doc officielle https://docs.qgis.org/3.16/fr/docs/user_manual/managing_data_source/create_layers.html#creating-virtual-layers)
Là vous pouvez utiliser plusieurs couches et le st_intersect.
Quand vous voulez changer de département vous n’avez qu’à éditer la requête sql