円と円の始まりと終わりを検出するアルゴリズムを探していますか?


24

グライダーパイロットからは、一定の間隔でGPSを修正するという形で多くのフライトデータを取得しています。飛行経路を分析し、グライダーのパイロットがサーマルを見つけたときに行う「旋回」の開始と終了を検出したいと思います。

理想的には、アルゴリズムは線上の開始点と終了点を与え、1つの「円」を定義します。これらのポイントは、GPS修正の1つと等しくなる可能性があり、補間する必要はありません。

飛行経路に沿って歩き、回転数を確認し、グライダーが旋回しているかどうかを判断するための基準を設定することができました。

PostGIS拡張機能を備えたPostgreSQLを使用しているため、この問題に対するより良いアプローチがあるかどうか興味がありました。私はすでに2つの線分の角度を計算する手順を持っています:

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
Barrett

あなたがた両方に感謝します。ハフ変換について見ていきます。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

それはかなり賢いように聞こえます。あるループが別のループと交差する状況をどのように処理しますか?または、ループを見つけたら最初のポイントをスキップしていますか?
ピーターホースボルモーラー

@PeterHorsbøllMøller交差点を探すのではなく、ラインがループを作成するタイミング(ST_BuildAreaは、ラインが閉じた領域を作成するときにのみtrueを返す)を分析します。
-kttii

@pgrossおっ!私は少し脇道に追い込まれ、開始/終了ポイントを忘れましたが、はい、ループが区別されるようになった今、それは十分に簡単な決定です。
kttii

@pgross各ループの開始/終了を特定するよりも、各ループのST_Centroidを特定する方が、サーマルのより合理的な位置を取得できると思われます。どう思いますか?もちろん、この関数は3つの統計すべてを提供できます。
-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.