从点确定多边形的切线(边界)线

时间:2017-12-06 14:02:52

标签: postgis

enter image description here

使用postgis有一种快速的方法从图中的点确定两条红色边界线吗?

1 个答案:

答案 0 :(得分:1)

例如,您可以根据自定义函数应用详细的here算法。下面是一个如何将其翻译成PostgreSQL / PostGIS的示例(该实现假设被测试的多边形只包含一个“组件”)。

CREATE OR REPLACE FUNCTION ST_IsLeft(P0 geometry(POINT), P1 geometry(POINT), P2 geometry(POINT)) RETURNS float AS $$
BEGIN
    RETURN (ST_X(P1) - ST_X(P0))*(ST_Y(P2) - ST_Y(P0)) - (ST_X(P2) - ST_X(P0))*(ST_Y(P1) - ST_Y(P0));
END
$$ LANGUAGE PLPGSQL;

CREATE OR REPLACE FUNCTION ST_IsAbove(P0 geometry(POINT), Vi geometry(POINT), Vj geometry(POINT)) RETURNS bool AS $$
BEGIN
    RETURN (ST_IsLeft(P0, Vi, Vj) > 0);
END
$$ LANGUAGE PLPGSQL;
CREATE OR REPLACE FUNCTION ST_IsBelow(P0 geometry(POINT), Vi geometry(POINT), Vj geometry(POINT)) RETURNS bool AS $$
BEGIN
    RETURN (ST_IsLeft(P0, Vi, Vj) < 0);
END
$$ LANGUAGE PLPGSQL;

CREATE OR REPLACE FUNCTION ST_TangentLine(P geometry(POINT), polygon geometry(POLYGON)) RETURNS SETOF geometry AS $$
DECLARE
    boundary geometry;
    Vi geometry;
    Vr geometry;
    Vl geometry;
    N int;
    i int;
    ePrev float;
    eNext float;
BEGIN
    N := ST_NPoints(polygon);
    i := 0;

    boundary := ST_Boundary(polygon);

    Vr := ST_PointN(boundary, 1);
    Vl := Vr;

    ePrev := ST_IsLeft(ST_PointN(boundary, 1), ST_PointN(boundary, 2), P);
    FOR i IN 2 .. (N-1)
    LOOP
        Vi := ST_PointN(boundary, i);
        eNext := ST_IsLeft(Vi, ST_PointN(boundary, i+1), P);

        IF ((ePrev <= 0) AND (eNext > 0)) THEN
            IF ( NOT ST_IsBelow(P, Vi, Vr) ) THEN
                Vr := Vi;
            END IF;
        ELSIF ((ePrev > 0) AND (eNext <= 0)) THEN
            IF ( NOT ST_IsAbove(P, Vi, Vl) ) THEN
                Vl := Vi;
            END IF;
        END IF;
        ePrev := eNext;
    END LOOP;

    RETURN NEXT ST_MakeLine(P, Vl);
    RETURN NEXT ST_MakeLine(P, Vr);
END
$$ LANGUAGE PLPGSQL;

DROP TABLE IF EXISTS polygons;
CREATE TABLE polygons(iid INTEGER, outline GEOMETRY);
INSERT INTO polygons VALUES (1, ST_GeomFromText('POLYGON((0 0, 1 0, 1 1, 0 1, 0 0))'));

SELECT iid, ST_AsText(ST_TangentLine(ST_MakePoint(-1, 0.5), outline)) FROM polygons;

然后返回

 iid |       st_astext
-----+------------------------
   1 | LINESTRING(-1 0.5,0 1)
   1 | LINESTRING(-1 0.5,0 0)