순환과 원의 시작과 끝을 감지하기 위해 알고리즘을 찾고 있습니까?


24

고정 간격으로 gps 수정 형식으로 글라이더 조종사로부터 많은 비행 데이터를 얻었습니다. 나는 비행 경로를 분석하고 글라이더 조종사가 열을 발견했을 때 수행 할 '순환'의 시작과 끝을 감지하고 싶습니다.

이상적으로, 알고리즘은 하나의 "원"을 정의하는 선의 시작과 끝점을 줄 것입니다. 이러한 점은 gps 수정 중 하나와 같을 수 있으며 보간 할 필요는 없습니다.

나는 단순히 비행 경로를 따라 걸을 수 있고, 회전율을 확인하고 글라이더가 돌고 있는지 여부를 결정하는 기준을 가지고 있습니다.

PostGIS 확장과 함께 PostgreSQL을 사용 하면서이 문제에 대한 더 나은 접근법이 있는지 궁금합니다. 이미 두 선분의 각도를 계산하는 절차가 있습니다.

CREATE OR REPLACE FUNCTION angle_between(
  _p1 GEOMETRY(PointZ,4326),
  _p2 GEOMETRY(PointZ,4326),
  _p3 GEOMETRY(PointZ,4326)
) RETURNS DECIMAL AS $$
DECLARE
  az1 FLOAT;
  az3 FLOAT;
BEGIN
  az1 = st_azimuth(_p2,_p1);
  az3 = st_azimuth(_p2,_p3);
IF az3 > az1 THEN
  RETURN (
      degrees(az3 - az1)::decimal - 180
  );
ELSE
  RETURN (
      degrees(az3 - az1)::decimal + 180
  );
END IF;
END;
$$ LANGUAGE plpgsql;

각도의 합이 360보다 크거나 -360 도보 다 작은 경우 모든 선분을 반복하고 점검 할 수 있어야합니다. 그런 다음 st_centroid를 사용하여 원의 중심을 감지 할 수 있습니다.

더 나은 접근법이 있습니까?


요청에 따라 예제 비행을 업로드했습니다 .

샘플 비행 경로


1
주위를 둘러 보면 허프 서클 트랜스 폼이 시작되었습니다. 비슷한 (폴리곤이 있지만) postgis 사용자 토론이 있습니다. lists.osgeo.org/pipermail/postgis-users/2015-February/...
— 바렛

둘 다 감사합니다. 허프 변환을 살펴 보겠습니다. osgeo.org에서의 토론은 내가 원을 올바르게 이해하면 원의 시작과 끝을 이미 알고 있다고 가정합니다.
— pgross


@DevdattaTengshe 네, 어쨌든 감사합니다. 그것은 스플라인과 곡률을 외부에서 계산 해야하는 접근법 일 것입니다. 외부 적으로는 데이터베이스에서 직접 프로 시저 또는 쿼리가 아닙니다. 항공편이 데이터베이스에 있으면 항공편이 변경되지 않으므로 옵션이됩니다.
— pgross

샘플 데이터를 .sql 파일로 게시 할 수 있습니까?
— dbaston

답변:


14

나는 이것에 대해 생각을 멈출 수 없었다 ... 나는 루프 계산을 수행하기 위해 저장 프로 시저를 생각해 낼 수 있었다. 예제 경로에는 109 개의 루프가 있습니다!

다음은 루프 중심이 빨간색으로 표시된 비행 지점입니다. 여기에 이미지 설명을 입력하십시오

기본적으로 캡처 된 순서대로 점을 통과하고 점을 반복 할 때 선을 만듭니다. 우리가 만들고있는 선이 ST_BuildArea를 사용하여 루프를 만들면 루프를 세고 그 지점부터 다시 선을 만들기 시작합니다.

이 함수는 루프 번호, 지오메트리, 시작 / 종료 지점 및 중심을 포함하는 각 루프의 레코드 세트를 반환합니다 (또한 일부를 정리하고 더 나은 변수 이름을 만들었습니다).

DROP FUNCTION test.find_loop_count(flightid int);

create function test.find_Loop_count(
    IN flightid      int,
    OUT loopnumber   int,
    OUT loopgeometry geometry,
    OUT loopstartend geometry,
    OUT loopcentroid geometry
    ) 
  RETURNS SETOF record AS
$BODY$

-- s schema 'test' must exist
-- a table 'points' of flight points must exist
--  we are going to iterate through the point path, building a line as we go
--   If the line creates a loop then we count a loop and start over building a new line
--     add the intersection point to the returning recordset
--     add the centroid of the loop to the resulting recordset
-- pass in the flight ID of the flight that you wish to count its loops for example:
--   SELECT * FROM find_loop_count(37);

DECLARE
    rPoint              RECORD;
    gSegment            geometry = NULL;
    gLastPoint          geometry = NULL;
    gLoopPolygon        geometry = NULL;
    gIntersectionPoint  geometry = NULL;
    gLoopCentroid       geometry = NULL;
    iLoops              integer := 0;
BEGIN
    -- for each line segment in Point Path
    FOR rPoint IN 
        WITH
            pts as (
                SELECT location as geom,datetime,row_number() OVER () as rnum 
                FROM test.points 
                WHERE flight_id=flightid
                ORDER BY 2) 
            SELECT ST_AsText(ST_MakeLine(ARRAY[a.geom, b.geom])) AS geom, a.rnum, b.rnum 
            FROM pts as a, pts as b 
            WHERE a.rnum = b.rnum-1 AND b.rnum > 1
        LOOP

        -- if this is the start of a new line then start the segment otherwise add the point to the segment
        if gSegment is null then
            gSegment=rPoint.geom;
        elseif rPoint.geom::geometry=gLastPoint::geometry then
        -- do not add this point to the segment because it is at the same location as the last point
        else
        -- add this point to the line
        gSegment=ST_Makeline(gSegment,rPoint.geom);
        end if;
        -- ST_BuildArea will return true if the line segment is noded and closed
        --  we must also flatten the line to 2D
        --  lets also make sure that there are more than three points in our line to define a loop
        gLoopPolygon=ST_BuildArea(ST_Node(ST_Force2D(gSegment)));
        if gLoopPolygon is not NULL and ST_Numpoints(gSegment) > 3 then
        -- we found a loop
        iLoops:=iLoops+1;

        -- get the intersection point (start/end)
        gIntersectionPoint=ST_Intersection(gSegment::geometry,rPoint.geom::geometry);

        -- get the centroid of the loop
        gLoopCentroid=ST_Centroid(gLoopPolygon);

        -- start building a new line
        gSegment=null;

        LOOPNUMBER   := iLoops;
        LOOPGEOMETRY := gLoopPolygon;
        LOOPSTARTEND := gIntersectionPoint;
        LOOPCENTROID := gLoopCentroid;

        RETURN NEXT;
        end if;
        -- keep track of last segment
        gLastPoint=rPoint.geom;
    END LOOP;
    RAISE NOTICE 'Total loop count is %.', iLoops;
END;
$BODY$
  LANGUAGE plpgsql STABLE
  COST 100
  ROWS 1000;

이것은 루프 카운트 만 반환하는 간단한 함수입니다.

DROP FUNCTION test.find_loop_count(flightid int);

create function test.find_Loop_count(flightid int) RETURNS integer AS $$
-- s schema 'test' must exist
-- a table 'points' of flight points must exist
--  we are going to iterate through the line path, building the line as we go
--   If the line creates a loop then we count a loop and start over building a new line
-- pass in the flight ID of the flight that you wish to count its loops for example:
--   SELECT find_loop_count(37);

DECLARE
    segment RECORD;
    s geometry = NULL;
    lastS geometry = NULL;
    b geometry = NULL;
    loops integer := 1;
BEGIN
    -- for each line segment is Point Path
    FOR segment IN 
        WITH
            pts as (
                SELECT location as geom,datetime,row_number() OVER () as rnum 
                FROM test.points 
                WHERE flight_id=flightid
                ORDER BY 2) 
            SELECT ST_AsText(ST_MakeLine(ARRAY[a.geom, b.geom])) AS geom, a.rnum, b.rnum 
            FROM pts as a, pts as b 
            WHERE a.rnum = b.rnum-1 AND b.rnum > 1
        LOOP

        -- if this is the start of a new line then make s be the segment otherwise add the segment to s
        if s is null then
            s=segment.geom;
        elseif segment.geom::geometry=lastS::geometry then
        else
            s=ST_Makeline(s,segment.geom);
        end if;
        -- ST_BuildArea will return true if the line segment is noded and closed
        --  we must also flatten the line to 2D
        b=ST_BuildArea(st_node(ST_Force2D(s)));
        if b is not NULL and st_numpoints(s) > 3 then
            RAISE NOTICE 's: %', s;
            RAISE NOTICE 'vvvvv %',st_numpoints(s);
            RAISE NOTICE 'I found a loop! Loop count is now %', loops;
            RAISE NOTICE '^^^^^';
            s=null;
            loops:=loops +1;
        end if;
        lastS=segment.geom;
    END LOOP;
    RAISE NOTICE 'Total loop count is %.', loops-1;
    RETURN loops-1;
END;
$$ LANGUAGE plpgsql;


이것은 매우 유망한 것으로 보입니다. 많은 감사합니다. 원 수에 관심이 없지만 시작 / 종료 지점에 관심이 있기 때문에 개선해야합니다. 그러나 그것은 쉽게 추측 할 수 있어야합니다.
— pgross September

꽤 영리하게 들립니다. 한 루프가 다른 루프와 교차하는 상황을 어떻게 처리합니까? 또는 루프를 찾은 후 초기 지점을 건너 뛰고 있습니까?
— 피터 Horsbøll Møller

@ PeterHorsbøllMøller 교차점을 찾는 대신 선이 루프를 만드는 시점을 분석합니다 (ST_BuildArea는 선이 닫힌 영역을 만들 때만 참을 반환 함).
— kttii

@pgross 죄송합니다! 나는 약간의 추억을 얻었고 시작 / 종료 지점을 잊어 버렸습니다. 그렇습니다. 루프가 구별된다는 것은 충분히 쉬운 결정입니다.
— kttii

@pgross 각 루프의 시작 / 끝을 찾는 대신 각 루프의 ST_Centroid를 찾아서 열의 적절한 위치를 얻는 것 같습니다. 어떻게 생각해? 물론이 함수는 세 가지 통계를 모두 제공 할 수 있습니다.
— kttii

3

gpx 파일에 악용 될 수있는 타임 스탬프가 있음을 알았습니다. 아마도 아래의 접근 방식이 효과적 일 수 있습니다.

Make a linesegement with Vi,Vi+1
Make it Polyline
Proceed to Vi+2,Vi+3 check intersection with Polyline
  if it intersects 
      find the point of intersection-Designate this as start/end point of the loop
      Make this intersection point as Vi and Vi+1 would next gpx point per time sequence  
  if the linesegement does not intersect with polyyline then  increment 'i' 

ST_BuildArea를 사용하도록 겹친 원으로 인해 ST_Intersects를 사용하는 것이 어렵다는 것을 알았습니다.
— kttii

당신의 대답이 일반적으로 같은 길에 있기 때문에 나는 당신에게 현상금을주었습니다.
— kttii
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.