¿Detecta si el punto está en el lado izquierdo o derecho de la línea en PostGIS?

16

Tengo una tabla de cadenas lineales y una tabla de puntos en postgis.

Sé la línea más cercana a cualquier punto dado. Lo que necesito saber es en qué "lado" de esa línea está el punto. Supongo que tengo que hacer eso creando una línea perpendicular desde el punto dado a la línea (punto más cercano en la línea) y luego comparar las coordenadas, pero no sé exactamente cómo hacerlo, y si es la forma correcta, ya que la línea cambia su dirección.

Hice una foto para ilustrar mi tarea.

ingrese la descripción de la imagen aquí

La línea en sí es negra, su dirección se muestra con flechas verdes. Necesito agregar una columna "lateral" a la tabla de puntos, para que los puntos rojos tengan el valor "derecho" y los puntos azules tengan el valor "izquierdo".

¿Alguien puede dar un ejemplo de código SQL para calcular un valor "lateral" de un punto?

mofoyoda
fuente

Respuestas:

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

Entonces, la idea es calcular el ángulo entre el segmento de línea más cercano y el vector desde el punto más cercano en la línea hasta su punto.

obtener un punto más cercano en una línea

select ST_ClosestPoint(line.geom, point.geom)

crea el vector desde el punto más cercano a tu punto

ST_MakeLine(cp.p, point.geom) vec

crea un vector entre tu línea

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

obtener la diferencia entre direcciones

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

Entonces, derecha e izquierda serán mayores que cero y menores que cero.

dmitry.v.kiselev
fuente
Gracias, parece una buena solución, pero no me gusta la parte * 1.01. ¿Se puede seleccionar el siguiente punto más cercano de la línea para que esta consulta sea más confiable?
mofoyoda
Estaba pensando en obtener el segmento más cercano, pero no tenemos esa función. Pero esta es una solución más confiable porque ST_LineInterpolate está dirigida, por lo que obtendrá el siguiente punto en adelante, la dirección de la línea, no solo la más cercana. Es posible obtener el siguiente nodo real, pero lo instaría a iterar sobre todos los nodos y descubrir si son los siguientes a lo largo de la línea o antes del punto más cercano en la línea.
dmitry.v.kiselev
Hola dmitry ¿Funcionará para un punto que está más allá de la línea si sabes a qué me refiero? Por ejemplo, el punto rojo superior izquierdo más, si fuera 1 cm más alto. En ese caso, el punto más cercano y el punto no formarán un ángulo recto con la línea original. ¿Funcionará este algoritmo en ese caso?
Jenia Ivanov
3
ST_Azimuth(h.vec)- es un pseudocódigo. h.vecy h.segson líneas, así que para ser precisos, debería ser algo así comoST_Azimuth(ST_StartPoint(h.vec), ST_EndPoint(h.vec))
dmitry.v.kiselev
2
la solución anterior no parece funcionar en los casos en que la línea es este-oeste con una orientación de exactamente 90 grados por alguna razón.
user7543032