Mencari Algoritma untuk mendeteksi lingkaran dan awal dan akhir lingkaran?


24

Saya punya banyak data penerbangan dari pilot glider dalam bentuk perbaikan gps dalam interval tetap. Saya ingin menganalisis jalur penerbangan dan mendeteksi awal dan akhir 'keliling' yang akan dilakukan pilot glider ketika dia menemukan termal.

Idealnya, suatu algoritma akan memberi saya titik awal dan akhir pada garis, mendefinisikan satu "lingkaran". Poin-poin ini bisa sama dengan salah satu perbaikan gps dan tidak perlu diinterpolasi.

Saya hanya bisa berjalan di sepanjang jalur penerbangan, memeriksa tingkat belokan dan memiliki beberapa kriteria untuk memutuskan apakah glider berputar atau tidak.

Karena saya menggunakan PostgreSQL dengan ekstensi PostGIS, saya ingin tahu apakah ada pendekatan yang lebih baik untuk masalah ini. Saya sudah memiliki prosedur untuk menghitung sudut dua segmen garis:

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;

Seharusnya dimungkinkan untuk mengulang semua segmen garis dan memeriksa, ketika jumlah sudut lebih besar dari 360 atau kurang dari -360 derajat. Lalu saya bisa menggunakan st_centroid untuk mendeteksi pusat lingkaran, jika perlu.

Apakah ada pendekatan yang lebih baik?


Seperti yang diminta, saya mengunggah contoh penerbangan .

Contoh jalur penerbangan


1
Melihat sekeliling menendang Hough Circle Transform. Ada diskusi pengguna postgis yang serupa (dengan poligon) di sini: lists.osgeo.org/pipermail/postgis-users/2015-Februari/...
Barrett

Terima kasih semuanya. Saya akan melihat Hough Transform. Diskusi di osgeo.org mengasumsikan bahwa saya sudah tahu di mana lingkaran dimulai dan berakhir, jika saya memahaminya dengan benar?
pgross


@DevdattaTengshe Ya, tapi terima kasih. Itu akan menjadi pendekatan di mana saya harus menghitung splines dan kelengkungan eksternal, bukan? Secara eksternal, maksud saya bukan sebagai prosedur atau permintaan langsung pada database. Karena penerbangan tidak berubah, begitu mereka berada di database, ini akan menjadi pilihan.
pgross

Bisakah Anda memposting beberapa data sampel sebagai file .sql?
dbaston

Jawaban:


14

Saya tidak bisa berhenti memikirkan hal ini ... Saya dapat membuat Prosedur Tersimpan untuk melakukan penghitungan loop. Contoh path berisi 109 loop!

Berikut adalah poin penerbangan yang ditunjukkan dengan loop centroid berwarna merah: masukkan deskripsi gambar di sini

Pada dasarnya, itu berjalan melalui titik-titik dalam urutan mereka ditangkap dan membangun garis saat melewati titik-titik. Ketika garis yang kita bangun membuat sebuah loop (menggunakan ST_BuildArea) maka kita menghitung loop dan mulai membangun garis lagi dari titik itu.

Fungsi ini mengembalikan recordset dari setiap loop yang berisi nomor loop, geometri, titik awal / akhir dan centroid-nya (saya juga membersihkannya sedikit dan membuat nama variabel yang lebih baik):

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;

Ini adalah fungsi sederhana untuk mengembalikan hanya jumlah loop:

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;


Ini terlihat sangat menjanjikan. Terima kasih banyak. Saya perlu meningkatkannya, karena saya tidak tertarik dengan jumlah lingkaran tetapi titik awal / akhir. Tapi itu seharusnya mudah untuk kembali kurasa.
pgross

Kedengarannya cukup pintar. Bagaimana cara menangani situasi di mana satu loop memotong loop lain? Atau apakah Anda melewatkan poin awal setelah Anda menemukan lingkaran?
Peter Horsbøll Møller

@ PeterHorsbøllMøller Ini menganalisis ketika garis membuat loop (ST_BuildArea hanya akan mengembalikan true ketika garis membuat area tertutup) daripada mencari persimpangan.
kttii

@ melintasi oops benar! Saya mendapatkan sedikit sidetracked dan lupa tentang titik awal / akhir tapi ya, itu tekad yang cukup mudah sekarang bahwa loop dibedakan.
kttii

@pgross Tampaknya bagi saya bahwa Anda mungkin akan mendapatkan lokasi termal yang lebih masuk akal dengan menempatkan ST_Centroid setiap loop daripada menemukan awal / akhir setiap loop. Apa yang kamu pikirkan? Tentu saja, fungsinya dapat menyediakan ketiga statistik.
kttii

3

Saya perhatikan bahwa file gpx memiliki cap waktu yang dapat dieksploitasi. Mungkin pendekatan di bawah ini bisa berhasil.

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' 

Saya merasa sulit untuk menggunakan ST_Intersects karena lingkaran yang tumpang tindih yang membuat saya menggunakan ST_BuildArea.
kttii

Saya memberi Anda hadiah karena jawaban Anda umumnya di jalur yang sama.
kttii
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.