Vous cherchez un algorithme pour détecter les cercles et le début et la fin du cercle?

24

J'ai beaucoup de données de vol de pilotes de planeurs sous forme de corrections GPS dans un intervalle fixe. Je voudrais analyser la trajectoire de vol et détecter le début et la fin des «cercles» que le pilote de planeur fera lorsqu'il trouvera des thermiques.

Idéalement, un algorithme me donnerait un point de départ et un point d'arrivée sur la ligne, définissant un "cercle". Ces points peuvent être égaux à l'un des correctifs GPS et n'ont pas besoin d'être interpolés.

Je pouvais simplement marcher le long de la trajectoire de vol, vérifier le taux de virage et avoir des critères pour décider si le planeur tourne ou non.

Comme j'utilise PostgreSQL avec l'extension PostGIS, j'étais curieux de savoir s'il existe une meilleure approche de ce problème. J'ai déjà une procédure pour calculer l'angle de deux segments de ligne:

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;

Il devrait être possible de boucler sur tous les segments de ligne et de vérifier, lorsque la somme des angles est supérieure à 360 ou inférieure à -360 degrés. Ensuite, je pourrais utiliser st_centroid pour détecter le centre du cercle, si nécessaire.

Est-ce qu'il y a une meilleure approche?


Comme demandé, j'ai téléchargé un exemple de vol .

Un exemple de trajectoire de vol

pgross
la source
1
En regardant autour, Hough Circle Transform a démarré. Il y a une discussion similaire (avec un polygone) avec les utilisateurs de postgis ici: lists.osgeo.org/pipermail/postgis-users/2015-F February
Barrett
Merci à vous deux. Je vais jeter un œil à la transformation de Hough. La discussion sur osgeo.org suppose que je sais déjà où commence et se termine le cercle, si je l'ai bien compris?
pgross
Avez-vous vu ceci: gis.stackexchange.com/questions/37058
Devdatta Tengshe
@DevdattaTengshe Oui, mais merci quand même. Ce serait une approche où je devrais calculer les splines et la courbure à l'extérieur, non? Par externe, je veux dire pas comme une procédure ou une requête directement sur la base de données. Comme les vols ne changent pas, une fois qu'ils sont dans la base de données, ce serait une option.
pgross
Pouvez-vous publier des exemples de données sous forme de fichier .sql?
dbaston

Réponses:

14

Je ne pouvais pas m'arrêter d'y penser ... J'ai pu trouver une procédure stockée pour faire le comptage de boucle. Le chemin d'exemple contient 109 boucles!

Voici les points de vol indiqués avec les centroïdes de boucle en rouge: entrez la description de l'image ici

Fondamentalement, il parcourt les points dans l'ordre où ils ont été capturés et construit une ligne à mesure qu'il parcourt les points. Lorsque la ligne que nous construisons crée une boucle (à l'aide de ST_BuildArea), nous comptons une boucle et recommençons à construire une ligne à partir de ce point.

Cette fonction retourne un jeu d'enregistrements de chaque boucle qui contient le numéro de boucle, sa géométrie, son point de début / fin et son centroïde (je l'ai également nettoyé un peu et fait de meilleurs noms de variables):

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;

Il s'agit d'une fonction simple pour renvoyer uniquement le nombre de boucles:

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;

kttii
la source
Cela semble très prometteur. Merci beaucoup. Je vais devoir l'améliorer, car je ne suis pas intéressé par le nombre de cercles mais par les points de début / fin. Mais cela devrait être facilement possible de revenir je suppose.
pgross
Cela semble assez intelligent. Comment gère-t-il la situation où une boucle croise une autre boucle? Ou sautez-vous les points initiaux une fois que vous avez localisé une boucle?
Peter Horsbøll Møller
@ PeterHorsbøllMøller Il analyse quand la ligne fait une boucle (ST_BuildArea ne retournera vrai que lorsque la ligne crée une zone fermée) plutôt que de chercher des intersections.
kttii
@pgross oups vrai! J'ai été un peu détourné et j'ai oublié les points de départ / fin, mais oui, c'est une détermination assez facile maintenant que les boucles sont distinguées.
kttii
@pgross Il me semble que vous obtiendrez probablement des emplacements plus raisonnables des thermiques en localisant le ST_Centroid de chaque boucle plutôt qu'en localisant le début / la fin de chaque boucle. Qu'est-ce que tu penses? Bien sûr, la fonction pourrait fournir les trois statistiques.
kttii
3

J'ai remarqué que le fichier gpx a un horodatage qui pourrait être exploité. Peut-être que l'approche ci-dessous pourrait fonctionner.

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' 
addcolor
la source
J'ai trouvé difficile d'utiliser ST_Intersects en raison du chevauchement des cercles qui m'a amené à utiliser ST_BuildArea.
kttii
Je vous ai donné la prime puisque votre réponse est généralement sur la même piste.
kttii