我一直不停地思考这个问题...我能够提出一个存储过程来进行循环计数。示例路径包含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;