Обнаружить, находится ли точка на левой или правой стороне линии в PostGIS?

16

У меня есть таблица строк и таблица точек в postgis.

Я знаю самую близкую линию к любой данной точке. Что мне нужно знать, так это то, на какой стороне этой линии находится точка. Я думаю, мне нужно сделать это путем создания перпендикулярной линии от заданной точки к линии (ближайшей точки на линии), а затем сравнить координаты, но я не знаю точно, как это сделать, и если это правильный путь, так как линия меняет свое направление.

Я сделал картинку, чтобы проиллюстрировать мою задачу.

введите описание изображения здесь

Сама линия черная, ее направление показано зелеными стрелками. Мне нужно добавить столбец "side" в таблицу точек, чтобы красные точки имели значение "right", а синие точки - "left".

Может кто-нибудь привести пример кода SQL для вычисления «побочного» значения точки?

mofoyoda
источник

Ответы:

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

Таким образом, идея состоит в том, чтобы вычислить угол между ближайшим отрезком линии и вектором от ближайшей точки на линии к вашей точке.

получить ближайшую точку на линии

select ST_ClosestPoint(line.geom, point.geom)

создать вектор от ближайшей точки к вашей точке

ST_MakeLine(cp.p, point.geom) vec

создать вектор среди вашей линии

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

получить разницу между направлениями

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

Так что справа и слева будет больше нуля и ниже нуля.

dmitry.v.kiselev
источник
Спасибо, это похоже на хорошее решение, но мне не нравится часть * 1.01. Можно ли выбрать следующую ближайшую точку линии, чтобы сделать этот запрос более надежным?
мофойода
Я думал о том, чтобы получить ближайший сегмент, но там у нас нет такой функции. Но это более надежное решение, потому что ST_LineInterpolate направлен так, что вы получите следующую точку вдоль направления линии, а не только ближайшую. Можно получить фактический следующий узел, но это побудит вас перебрать все узлы и выяснить, находятся ли они рядом вдоль линии или перед ближайшей точкой на линии.
dmitry.v.kiselev
Привет Дмитрий. Будет ли это работать для точки, которая находится за чертой, если вы знаете, что я имею в виду. Например, верхняя левая самая красная точка, если она была на 1 см выше. В этом случае closestPoint и точка не будут составлять прямой угол с исходной линией. Будет ли этот алгоритм работать в этом случае?
Женя Иванов
3
ST_Azimuth(h.vec)- это псевдокод. h.vecи h.segэто строки, так что если быть точным, то это должно быть что-то вродеST_Azimuth(ST_StartPoint(h.vec), ST_EndPoint(h.vec))
dmitry.v.kiselev
2
вышеупомянутое решение, по-видимому, не работает в тех случаях, когда линия находится на востоке-западе, по какой-то причине она имеет угол 90 градусов.
user7543032