Daireyi daire içine almayı ve başlangıcı ve bitişi saptamak için algoritma mı arıyorsunuz?


24

Planör pilotlarından sabit aralıklarla gps düzeltmeleri şeklinde birçok uçuş verisi aldım. Uçuş yolunu analiz etmek ve planör pilotunun termik bulduğunda yapacağı 'daire çizmenin' başlangıcını ve sonunu tespit etmek istiyorum.

İdeal olarak, bir algoritma bana bir "daire" tanımlayan, hatta bir başlangıç ​​ve bitiş noktası verecektir. Bu noktalar gps düzeltmelerinden birine eşit olabilir ve enterpolasyona gerek yoktur.

Uçuş yolunda yürüyebilir, dönme oranını kontrol edebilir ve planörün dönüp dönmediğine karar vermek için bazı kriterler bulabilirim.

PostgreSQL'i PostGIS uzantılı kullandığım için, bu konuda daha iyi bir yaklaşım olup olmadığını merak ediyordum. İki satır segmentinin açısını hesaplamak için zaten bir prosedürüm var:

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;

Tüm çizgi parçalarının üzerinden döngü oluşturmak ve açıların toplamının 360'dan büyük veya -360 dereceden düşük olduğunu kontrol etmek mümkün olmalıdır. Sonra gerekirse dairenin merkezini tespit etmek için st_centroid kullanabilirim.

Daha iyi bir yaklaşım var mı?


İstediğim gibi, örnek bir uçuş yükledim .

Örnek bir uçuş yolu


1
Etrafa tepeden bakma Hough Circle Transform. Burada benzer (çokgen olsa da) postgis kullanıcı tartışması var: lists.osgeo.org/pipermail/postgis-users/2015- Şubat /…
Barrett

İkinize de teşekkürler. Hough dönüşümüne bir göz atacağım. Osgeo.org'daki tartışma, doğru anladıysam, çemberin nerede başladığını ve bittiğini zaten biliyor mu?
pgross


@DevdattaTengshe Evet, yine de teşekkürler. Bu, eğri çizgileri ve eğriliği dışarıdan hesaplamak zorunda kalacağım bir yaklaşım olurdu, değil mi? Harici olarak, doğrudan veritabanında bir yordam veya sorgu olarak değil. Uçuşlar değişmediğinden, veritabanına girdikten sonra bu bir seçenek olacaktır.
pgross

Bazı örnek verileri bir .sql dosyası olarak gönderebilir misiniz?
dbaston

Yanıtlar:


14

Bunu düşünmeden edemedim ... Döngü sayımı yapmak için Saklı Bir Prosedür ile gelebildim. Örnek yol 109 döngüler içeriyor!

Kırmızı renkli loop centroidlerle gösterilen uçuş noktaları: görüntü tanımını buraya girin

Temel olarak, yakalandıkları sırayla noktalar arasında ilerler ve noktalar arasında ilerledikçe bir çizgi oluşturur. Yaptığımız çizgi bir döngü yarattığında (ST_BuildArea kullanarak) bir döngü sayıp tekrar bu noktadan bir çizgi oluşturmaya başlarız.

Bu işlev, döngü numarasını, geometrisini, başlangıç ​​/ bitiş noktasını ve centroidini içeren her bir döngünün bir kayıt kümesini döndürür (Ben de biraz temizledim ve daha iyi değişken isimleri yaptım):

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;

Bu sadece döngü sayısını döndürmek için basit bir fonksiyondur:

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;


Bu çok umut verici görünüyor. Çok sağol. Çevrelerin sayısıyla değil, başlangıç ​​/ bitiş noktalarıyla ilgilenmediğim için onu geliştirmem gerekecek. Ama bu kolayca geri dönmek mümkün olmalı sanırım.
pgross

Kulağa çok zekice geliyor. Bir döngünün diğer döngünün kesiştiği durumu nasıl ele alır? Yoksa bir döngü bulduğunuzda başlangıç ​​noktalarını atlıyor musunuz?
Peter Horsbøll Møller

@ PeterHorsbøllMøller Çizginin ne zaman bir döngü oluşturduğunu analiz eder (ST_BuildArea, kesişimleri aramak yerine çizginin kapalı bir alan oluşturduğu zaman gerçek olur).
kttii

@pgross ayy doğru! Biraz üzüldüm ve başlangıç ​​/ bitiş noktalarını unuttum ama evet, bu artık döngülerin ayırt edilebildiği kadar kolay bir karar.
kttii

@pgross Bana göre, her bir döngünün başlangıcını / bitişini bulmak yerine her bir döngünün ST_Centroid'sini bularak termallerin daha makul yerlerini elde edersiniz. Ne düşünüyorsun? Elbette, fonksiyon üç istatistiği de sağlayabilir.
kttii

3

GPX dosyasının sömürülebilecek zaman damgası olduğunu fark ettim. Belki aşağıdaki yaklaşım işe yarayabilir.

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 kullanmama neden olan çakışan daireler nedeniyle ST_Iersters kullanımını zor buldum.
kttii

Cevabınız genellikle aynı yolda olduğundan, size ödül verdim.
kttii
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.