Détecter si le point se trouve à gauche ou à droite de la ligne dans PostGIS?

16

J'ai une table linestring et une table de points en postgis.

Je connais la ligne la plus proche d'un point donné. Ce que je dois savoir, c'est de quel «côté» de cette ligne se trouve le point. Je suppose que je dois le faire en créant une ligne perpendiculaire d'un point donné à la ligne (point le plus proche sur la ligne) puis comparer les coordonnées, mais je ne sais pas exactement comment faire cela, et si c'est la bonne façon, puisque la ligne change de direction.

J'ai fait une photo pour illustrer ma tâche.

entrez la description de l'image ici

La ligne elle-même est noire, sa direction est indiquée par des flèches vertes. J'ai besoin d'ajouter une colonne "latérale" à la table de points, de sorte que les points rouges doivent avoir la valeur "droite" et les points bleus doivent avoir la valeur "gauche".

Quelqu'un peut-il donner un exemple de code SQL de calcul d'une valeur "latérale" d'un point?

mofoyoda
la source

Réponses:

12
select (ST_Azimuth(h.vec) - ST_Azimuth(h.seg))
from (
    select 
        ST_MakeLine(cp.p, point.geom) vec,
        ST_MakeLine(cp.p, 
            ST_LineInterpolatePoint(
                line.geom, 
                ST_LineLocatePoint(line.geom, cp.p) * 1.01)
        ) seg
        from (
            select 
                ST_ClosestPoint(line.geom, point.geom)
        ) p as cp
    ) as h

L'idée est donc de calculer l'angle entre le segment de ligne le plus proche et le vecteur du point le plus proche sur la ligne jusqu'à votre point.

obtenir un point le plus proche sur une ligne

select ST_ClosestPoint(line.geom, point.geom)

créer le vecteur du point le plus proche de votre point

ST_MakeLine(cp.p, point.geom) vec

créez un vecteur parmi votre ligne

ST_MakeLine(
    --original point
    cp.p, 
    --find a point next to the closest point on line
    ST_LineInterpolatePoint(line.geom, 
         ST_LineLocatePoint(line.geom, cp.p) * 1.01)) seg

obtenir la différence entre les directions

ST_Azimuth(h.vec) - ST_Azimuth(h.seg)

Donc, la droite et la gauche seront supérieures à zéro et inférieures à zéro.

dmitry.v.kiselev
la source
Merci, cela semble être une bonne solution, mais je n'aime pas la partie * 1.01. Le prochain point le plus proche de la ligne peut-il être sélectionné pour rendre cette requête plus fiable?
mofoyoda
Je pensais à obtenir le segment le plus proche, mais il nous n'existe pas une telle fonction. Mais c'est une solution plus fiable car ST_LineInterpolate est dirigé de sorte que vous obtiendrez le point suivant dans la direction de la ligne, pas seulement le plus proche. Il est possible d'obtenir le nœud suivant, mais cela vous inciterait à parcourir tous les nœuds et à découvrir s'ils sont les suivants le long de la ligne ou avant le point en ligne le plus proche.
dmitry.v.kiselev
Salut Dmitry. Est-ce que cela fonctionnera pour un point qui est au-delà de la ligne si vous voyez ce que je veux dire. Par exemple, le point rouge en haut à gauche, s'il était supérieur de 1 cm. Dans ce cas, le point le plus proche et le point ne feront pas un angle droit avec la ligne d'origine. Cet algorithme fonctionnera-t-il dans ce cas?
Jenia Ivanov
3
ST_Azimuth(h.vec)- est un pseudocode. h.vecet h.segsont des lignes, donc pour être précis, cela devrait être quelque chose commeST_Azimuth(ST_StartPoint(h.vec), ST_EndPoint(h.vec))
dmitry.v.kiselev
2
la solution ci-dessus ne semble pas fonctionner dans les cas où la ligne est est-ouest a un relèvement d'exactement 90 degrés pour une raison quelconque.
user7543032