Tìm kiếm thuật toán để phát hiện vòng tròn và bắt đầu và kết thúc vòng tròn?


24

Tôi đã nhận được rất nhiều dữ liệu chuyến bay từ các phi công tàu lượn dưới dạng sửa lỗi gps trong một khoảng thời gian cố định. Tôi muốn phân tích đường bay và phát hiện điểm bắt đầu và kết thúc của 'vòng tròn' mà phi công tàu lượn sẽ làm khi anh ta tìm thấy nhiệt.

Lý tưởng nhất, một thuật toán sẽ cho tôi một điểm bắt đầu và điểm kết thúc trên dòng, xác định một "vòng tròn". Những điểm này có thể bằng một trong các bản sửa lỗi gps và không cần phải nội suy.

Tôi chỉ đơn giản là có thể đi bộ dọc theo đường bay, kiểm tra tốc độ quay vòng và có một số tiêu chí để quyết định xem tàu ​​lượn có bay vòng quanh hay không.

Khi tôi đang sử dụng PostgreSQL với phần mở rộng PostGIS, tôi đã tò mò liệu có cách tiếp cận nào tốt hơn cho vấn đề này không. Tôi đã có một quy trình để tính góc của hai đoạn đường:

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;

Có thể lặp qua tất cả các phân đoạn dòng và kiểm tra, khi tổng các góc lớn hơn 360 hoặc nhỏ hơn -360 độ. Sau đó, tôi có thể sử dụng st_centroid để phát hiện trung tâm của vòng tròn, nếu cần.

Có một cách tiếp cận tốt hơn?


Theo yêu cầu, tôi đã tải lên một chuyến bay ví dụ .

Một đường bay mẫu


1
Nhìn xung quanh đá lên Hough Circle Transform. Có một cuộc thảo luận người dùng postgis
Barrett

Cảm ơn cả hai người. Tôi sẽ có một cái nhìn về Hough Transform. Cuộc thảo luận tại osgeo.org giả định rằng tôi đã biết vòng tròn bắt đầu và kết thúc ở đâu, nếu tôi hiểu đúng?
trải qua

Bạn đã thấy cái này chưa: gis.stackexchange.com/questions/37058
Devdatta Tengshe

@DevdattaTengshe Vâng, nhưng dù sao cũng cảm ơn. Đó sẽ là một cách tiếp cận mà tôi sẽ phải tính toán các spline và độ cong bên ngoài, phải không? Bởi bên ngoài, tôi có nghĩa là không phải là một thủ tục hoặc truy vấn trực tiếp trên cơ sở dữ liệu. Vì các chuyến bay không thay đổi, một khi chúng ở trong cơ sở dữ liệu, đây sẽ là một lựa chọn.
trải qua

Bạn có thể đăng một số dữ liệu mẫu dưới dạng tệp .sql không?
dbaston

Câu trả lời:


14

Tôi không thể ngừng suy nghĩ về điều này ... Tôi đã có thể đưa ra một Quy trình được lưu trữ để thực hiện việc đếm vòng lặp. Đường dẫn ví dụ chứa 109 vòng lặp!

Dưới đây là các điểm bay được hiển thị với các tâm vòng lặp màu đỏ: nhập mô tả hình ảnh ở đây

Về cơ bản, nó chạy qua các điểm theo thứ tự chúng đã bị bắt và xây dựng một đường khi nó lặp qua các điểm. Khi dòng chúng tôi đang xây dựng tạo một vòng lặp (sử dụng ST_BuildArea) thì chúng tôi sẽ đếm một vòng lặp và bắt đầu xây dựng lại một dòng từ điểm đó.

Hàm này trả về một recordset của mỗi vòng lặp chứa số vòng lặp, hình dạng của nó, điểm bắt đầu / điểm kết thúc và tâm của nó (Tôi cũng đã làm sạch nó một chút và tạo ra các tên biến tốt hơn):

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;

Đây là một hàm đơn giản để chỉ trả về số vòng lặp:

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;


Điều này có vẻ rất hứa hẹn. Cảm ơn nhiều. Tôi sẽ cần phải tăng cường nó, vì tôi không quan tâm đến số lượng vòng tròn nhưng điểm bắt đầu / kết thúc. Nhưng điều đó có thể dễ dàng trở lại tôi đoán.
trải qua

Nghe có vẻ khá thông minh. Làm thế nào để nó xử lý tình huống trong đó một vòng lặp giao nhau với một vòng lặp khác? Hoặc bạn đang bỏ qua các điểm ban đầu khi bạn xác định vị trí một vòng lặp?
Peter Horsbøll Møller

@ PeterHorsbøllMøller Nó phân tích khi dòng tạo một vòng lặp (ST_BuildArea sẽ chỉ trả về đúng khi dòng tạo ra một vùng kín) thay vì tìm kiếm các giao lộ.
kttii

@pgross oops đúng! Tôi đã có một chút lúng túng và quên mất điểm bắt đầu / kết thúc nhưng vâng, đó là một quyết định đủ dễ dàng bây giờ rằng các vòng lặp được phân biệt.
kttii

@pgross Dường như với tôi rằng bạn có thể sẽ có được vị trí hợp lý hơn của các thermals bằng cách định vị ST_Centroid của mỗi vòng lặp thay vì định vị điểm bắt đầu / kết thúc của mỗi vòng lặp. Bạn nghĩ sao? Tất nhiên, chức năng có thể cung cấp cả ba số liệu thống kê.
kttii

3

Tôi nhận thấy rằng tệp gpx có dấu thời gian có thể được khai thác. Có lẽ cách tiếp cận dưới đây có thể làm việc.

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' 

Tôi thấy khó sử dụng ST_Intersects vì các vòng tròn chồng chéo khiến tôi sử dụng ST_BuildArea.
kttii

Tôi đã đưa cho bạn tiền thưởng vì câu trả lời của bạn thường nằm trên cùng một bài hát.
kttii
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.