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


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.