最近項目上有一個計算兩點最短路徑的需求,即就是類似於百度地圖的路徑規划問題,小編研究了一段時間,並參考了相關資料,基於postgresql+postgis+pgrouting實現了簡單的路徑規划,計算的結果是組成最短路徑的所有線路的集合,話不多說,直接上主要的存儲過程:
CREATE OR REPLACE FUNCTION public.pgr_shortest_road( IN startx double precision, IN starty double precision, IN endx double precision, IN endy double precision, OUT road_name character varying, OUT v_shpath character varying, OUT cost double precision) RETURNS SETOF record AS $BODY$ declare v_startLine geometry;--離起點最近的線 v_endLine geometry;--離終點最近的線 v_startTarget integer;--距離起點最近線的終點 v_endSource integer;--距離終點最近線的起點 v_statpoint geometry;--在v_startLine上距離起點最近的點 v_endpoint geometry;--在v_endLine上距離終點最近的點 v_res geometry;--最短路徑分析結果 v_perStart float;--v_statpoint在v_res上的百分比 v_perEnd float;--v_endpoint在v_res上的百分比 v_rec record; first_name varchar; end_name varchar; first_cost double precision; end_cost double precision; begin --查詢離起點最近的線 execute 'select geom,target,name from china_road where ST_DWithin(geom,ST_Geometryfromtext(''point('|| startx ||' ' || starty||')''),0.01) order by ST_Distance(geom,ST_GeometryFromText(''point('|| startx ||' '|| starty ||')'')) limit 1' into v_startLine ,v_startTarget,first_name; --查詢離終點最近的線 execute 'select geom,source,name from china_road where ST_DWithin(geom,ST_Geometryfromtext(''point('|| endx || ' ' || endy ||')''),0.01) order by ST_Distance(geom,ST_GeometryFromText(''point('|| endx ||' ' || endy ||')'')) limit 1' into v_endLine,v_endSource,end_name; --如果沒找到最近的線,就返回null if (v_startLine is null) or (v_endLine is null) then return; end if ; select ST_ClosestPoint(v_startLine, ST_Geometryfromtext('point('|| startx ||' ' || starty ||')')) into v_statpoint; select ST_ClosestPoint(v_endLine, ST_GeometryFromText('point('|| endx ||' ' || endy ||')')) into v_endpoint; --計算距離起點最近線上的點在該線中的位置 select ST_Line_Locate_Point(st_linemerge(v_startLine), v_statpoint) into v_perStart; select ST_Line_Locate_Point(st_linemerge(v_endLine), v_endpoint) into v_perEnd; select ST_Distance_Sphere(v_statpoint,ST_PointN(ST_GeometryN(v_startLine,1), ST_NumPoints(ST_GeometryN(v_startLine,1)))) into first_cost; select ST_Distance_Sphere(ST_PointN(ST_GeometryN(v_endLine,1),1),v_endpoint) into end_cost; if (ST_Intersects(st_geomfromtext('point('|| startx ||' '|| starty ||') '), v_startLine) and ST_Intersects(st_geomfromtext('point('|| endx ||' '|| endy ||') '), v_startLine)) then select ST_Distance_Sphere(v_statpoint, v_endpoint) into first_cost; select ST_Line_Locate_Point(st_linemerge(v_startLine), v_endpoint) into v_perEnd; for v_rec in select ST_Line_SubString(st_linemerge(v_startLine), v_perStart,v_perEnd) as point,COALESCE(end_name,'無名路') as name,end_cost as cost loop v_shPath:= ST_AsGeoJSON(v_rec.point); cost:= v_rec.cost; road_name:= v_rec.name; return next; end loop; return; end if; --最短路徑 for v_rec in (select ST_Line_SubString(st_linemerge(v_startLine),v_perStart,1) as point,COALESCE(first_name,'無名路') as name,first_cost as cost union all SELECT st_linemerge(b.geom) as point,COALESCE(b.name,'無名路') as name,b.length as cost FROM pgr_dijkstra( 'SELECT gid as id, source, target, length as cost FROM china_road where st_intersects(geom,st_buffer(st_linefromtext(''linestring('||startx||' ' || starty ||','|| endx ||' ' || endy ||')''),0.05))', v_startTarget, v_endSource , false ) a, china_road b WHERE a.edge = b.gid union all select ST_Line_SubString(st_linemerge(v_endLine),0,v_perEnd) as point,COALESCE(end_name,'無名路') as name,end_cost as cost) loop v_shPath:= ST_AsGeoJSON(v_rec.point); cost:= v_rec.cost; road_name:= v_rec.name; return next; end loop; end; $BODY$ LANGUAGE plpgsql VOLATILE STRICT;
如有問題,歡迎留言交流!
