Я не міг перестати думати про це ... Мені вдалося придумати збережену процедуру, щоб зробити цикл підрахунку. Приклад шляху містить 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;