【學習筆記之Openlayers3】路徑分析篇(第六篇) -开发者知识库

【學習筆記之Openlayers3】路徑分析篇(第六篇) -开发者知识库,第1张

最佳路徑分析

最近在使用openlayer3進行項目開發的時候,由於項目需求要實現最佳路徑分析功能。原先沒做過,找了網上的資料,然后現在將自己做的總結一下。


相關算法

首先找了相關的路徑分析算法一種是迪傑克斯特拉(Dijkstra)算法
還有一種是Floyd-Warshall算法 ,目前比較常用的是迪傑克斯特拉(Dijkstra)算法進行路徑分析的。

如何實現路徑分析

首先開發環境是:openlayers3 geoserver postgresql;
實現步驟:

1.首先要有一張路網的表
2.postgresql添加相應擴展
3.形成拓撲關系
4.編寫相應的最短路徑存儲過程
5.在geoserver中調用該存儲過程形成最短路徑圖層

步驟詳解

一、路網表

    首先你要有路網表才能進行路徑分析,這個就不多贅述了

二、 添加擴展

 用postgresql的"SQL Shell"向具體要使用的數據庫添加擴展 
CREATE EXTENSION postgis;
CREATE EXTENSION pgrouting;
CREATE EXTENSION postgis_topology;
CREATE EXTENSION fuzzystrmatch;
CREATE EXTENSION postgis_tiger_geocoder;
CREATE EXTENSION address_standardizer;

三、 形成拓撲關系

1.創建拓撲結構

-- 添加起點id

ALTER TABLE public.tablename ADD COLUMN source integer;

-- 添加終點id

ALTER TABLE public.tablename ADD COLUMN target integer;

-- 添加道路權重值

ALTER TABLE public.tablename ADD COLUMN length double precision;
-- 創建拓撲結構 0.00001 這個可以自己定義這個數值越大越模糊,越小越精細,這個看實際需求吧
SELECT pgr_createTopology('public.tablename ',0.00001, 'geom', 'gid');
-- 創建索引
CREATE INDEX source_idx ON tablename("source");

CREATE INDEX target_idx ON tablename("target");
-- 為length賦值 這個將會作為路徑分析中的權值
update tablename set length =st_length(geom);

2.為路網表添加字段:添加字段為了存儲路網表中每條記錄的道路的起點經緯度以及終點經緯度

ALTER TABLE tablename ADD COLUMN x1 double precision;

ALTER TABLE tablename ADD COLUMN y1 double precision;

ALTER TABLE tablename ADD COLUMN x2 double precision;

ALTER TABLE tablename ADD COLUMN y2 double precision;
-- 為新建的字段賦值
UPDATE tablename SET x1 =ST_x(ST_PointN(geom, 1));

UPDATE tablename SET y1 =ST_y(ST_PointN(geom, 1));

UPDATE tablename SET x2 =ST_x(ST_PointN(geom, ST_NumPoints(geom)));

UPDATE tablename SET y2 =ST_y(ST_PointN(geom, ST_NumPoints(geom)));

四、存儲過程

-- FUNCTION: public.pgr_fromatob(character varying, double precision, double precision, double precision, double precision, text)

-- DROP FUNCTION public.pgr_fromatob(character varying, double precision, double precision, double precision, double precision, text);

CREATE OR REPLACE FUNCTION public.pgr_fromatob(
tbl character varying,
startx double precision,
starty double precision,
endx double precision,
endy double precision,
pass text)
RETURNS geometry
LANGUAGE 'plpgsql'
COST 100.0
VOLATILE NOT LEAKPROOF STRICT
AS $function$



declare
v_startLine geometry;-- 離起點最近的線
v_endLine geometry;-- 離終點最近的線

v_startTarget integer;-- 距離起點 最近線的終點
v_startSource integer;-- 距離起點 最近線的起點
v_endTarget integer;-- 距離終點 最近線的終點
v_endSource integer;-- 距離終點 最近線的起點

v_statpoint geometry;-- 在v_startLine上距離起點最近的點
v_endpoint geometry;-- 在v_endLine上距離終點最近的點

v_passarray text[]; -- 途徑點point數組
v_passids integer[]; -- 途徑點target id
v_passSources integer[]; -- 途徑點source id
v_passTarget integer; -- 途徑線段的終點
v_passSource integer; -- 途徑線的起點
v_passLine geometry; -- 距離途徑點最近的線
v_passLines geometry[]; -- 所有途徑點的線數組

v_res geometry;-- 最短路徑分析結果
v_passres geometry; -- 途徑點路徑分析結果
v_ress geometry[]; -- 包含途徑點最短路徑的分析結果

v_perStart float;-- v_statpoint在v_res上的百分比
v_perEnd float;-- v_endpoint在v_res上的百分比

v_shPath geometry;-- 最終結果
tempnode float;
passp varchar;

v_tempTarget int;

begin

-- 查詢離起點最近的線
execute 'select geom ,target,source from ' ||tbl||
' where
ST_DWithin(geom,ST_Geometryfromtext(''point('|| startx ||' ' || starty||')'',4326),0.1)
order by ST_Distance(geom,ST_GeometryFromText(''point('|| startx ||' '|| starty ||')'',4326)) limit 1'
into v_startLine ,v_startTarget,v_startSource;

-- 查詢離終點最近的線
execute 'select geom,target,source from ' ||tbl||
' where ST_DWithin(geom,ST_Geometryfromtext(''point('|| endx || ' ' || endy ||')'',4326),0.1)
order by ST_Distance(geom,ST_GeometryFromText(''point('|| endx ||' ' || endy ||')'',4326)) limit 1'
into v_endLine,v_endTarget,v_endSource;

-- 如果沒找到最近的線,就返回null
IF (v_startLine is null) or (v_endLine is null) THEN
return null;
END IF ;

-- 判斷是否存在途徑點
IF (pass !='') THEN
-- 對參數進行處理形成array 參數樣例 'x1,y1 x2,y2 '
select regexp_split_to_array(pass, E'\s ') into v_passarray;

FOR i IN array_lower(v_passarray, 1) .. array_upper(v_passarray, 1)
LOOP
passp :=v_passarray[i];

IF (passp !='') THEN
execute 'select geom,target,source from ' ||tbl||
' where
ST_DWithin(geom,ST_Geometryfromtext(''point('|| split_part(passp,',',1) ||' ' || split_part(passp,',',2)||')'',4326),0.1)
order by ST_Distance(geom,ST_GeometryFromText(''point('|| split_part(passp,',',1) ||' '|| split_part(passp,',',2) ||')'',4326)) limit 1'
into v_passLine,v_passTarget,v_passSource;

select array_append(v_passids,v_passTarget) into v_passids;
select array_append(v_passSources,v_passSource) into v_passSources;
select array_append(v_passLines,v_passLine) into v_passLines;

end if ;

END Loop;
end if ;


select array_append(v_passSources,v_endSource) into v_passSources; -- 將source傳入source
select ST_ClosestPoint(v_startLine, ST_Geometryfromtext('point('|| startx ||' ' || starty ||')',4326)) into v_statpoint;
select ST_ClosestPoint(v_endLine, ST_GeometryFromText('point('|| endx ||' ' || endy ||')',4326)) into v_endpoint;


IF (pass !='') THEN -- 判斷是否包含途徑點
-- 將途徑點進行循環計算每個途徑點間的最短路徑
-- 4403000314 是小路的編碼,分析中除去小路的影響
FOR i IN array_lower(v_passSources, 1) .. array_upper(v_passSources, 1)
LOOP
IF i=1 THEN
EXECUTE 'SELECT st_linemerge(st_union(b.geom)) ' ||
'FROM pgr_kdijkstraPath(
''SELECT gid as id, source, target, length as cost FROM public.' || quote_ident(tbl) ||' where fcode !='''''||'4403000314'||''''''','
||v_startSource || ', ' ||'array['||v_passSources[i]||'] , false, false
) a, '
|| quote_ident(tbl) || ' b
WHERE a.id3=b.gid
GROUP by id1
ORDER by id1' into v_passres;
ELSE
-- 判斷當前的途徑點后有沒有可以連接的線(即判斷這個途徑點所在線段是否為'死胡同')
EXECUTE 'select * from hangzhouload_1 t where t.source= '||v_passids[i-1]||'' into v_tempTarget;
IF (v_tempTarget is null) THEN
-- 如果為'死胡同'則原路返回,以該條線段起始點為節點進行路徑分析
v_tempTarget:=v_passSources[i-1];
ELSE
v_tempTarget:=v_passids[i-1];
END IF;
EXECUTE 'SELECT st_linemerge(st_union(b.geom)) ' ||
'FROM pgr_kdijkstraPath(
''SELECT gid as id, source, target, length as cost FROM ' || tbl ||' where fcode !='''''||'4403000314'||''''''','
||v_tempTarget || ', ' ||'array['||v_passSources[i]||'] , false, false
) a, '
|| tbl || ' b
WHERE a.id3=b.gid
GROUP by id1
ORDER by id1' into v_passres;
END IF;
-- 將分析出的路段以及途徑點所在(附近的路徑)添加到數組中
select array_append(v_ress,v_passres) into v_ress;
-- 排除最后一條線段(終點所在線段/附近線段)
IF(i<array_upper(v_passSources, 1)) THEN
select array_append(v_ress,v_passLines[i]) into v_ress;
END IF;
END Loop;
-- 如果排除小路找不到最佳路徑,則放開對小路的限制
IF (v_ress is null) THEN

FOR i IN array_lower(v_passSources, 1) .. array_upper(v_passSources, 1)
LOOP
IF i=1 THEN
EXECUTE 'SELECT st_linemerge(st_union(b.geom)) ' ||
'FROM pgr_kdijkstraPath(
''SELECT gid as id, source, target, length as cost FROM public.' || quote_ident(tbl) ||''','
||v_startTarget || ', ' ||'array['||v_passSources[i]||'] , false, false
) a, '
|| quote_ident(tbl) || ' b
WHERE a.id3=b.gid
GROUP by id1
ORDER by id1' into v_passres;
ELSE
EXECUTE 'SELECT st_linemerge(st_union(b.geom)) ' ||
'FROM pgr_kdijkstraPath(
''SELECT gid as id, source, target, length as cost FROM ' || tbl ||''','
||v_passids[i-1] || ', ' ||'array['||v_passSources[i]||'] , false, false
) a, '
|| tbl || ' b
WHERE a.id3=b.gid
GROUP by id1
ORDER by id1' into v_passres;
END IF;
select array_append(v_ress,v_passres) into v_ress;
END LOOP;

END IF;
-- 處理線段

select st_linemerge(ST_Union(array[v_ress])) into v_res;

ELSE

EXECUTE 'SELECT st_linemerge(st_union(b.geom)) ' ||
'FROM pgr_kdijkstraPath(
''SELECT gid as id, source, target, length as cost FROM ' || tbl ||' where fcode !='''''||'4403000314'||''''''','
||v_startTarget || ', ' ||'array['||v_endSource||'] , false, false
) a, '
|| tbl || ' b
WHERE a.id3=b.gid
GROUP by id1
ORDER by id1' into v_res ;
-- 如果找不到最短路徑,就返回null
IF(v_res is null) THEN
EXECUTE 'SELECT st_linemerge(st_union(b.geom)) ' ||
'FROM pgr_kdijkstraPath(
''SELECT gid as id, source, target, length as cost FROM ' || tbl ||''','
||v_startTarget || ', ' ||'array['||v_endSource||'] , false, false
) a, '
|| tbl || ' b
WHERE a.id3=b.gid
GROUP by id1
ORDER by id1' into v_res ;
END IF;
-- 將v_res,v_startLine,v_endLine進行拼接
select st_linemerge(ST_Union(array[v_res,v_startLine,v_endLine])) into v_res;
END IF;


-- 最短路徑
-- 判斷當前路徑線類型
IF(GeometryType(v_res)='MULTILINESTRING') THEN
-- 截取起始段線段
select ST_LineLocatePoint(v_startLine, v_statpoint) into v_perStart;

select ST_LineSubstring(v_startLine,0,v_perStart ) into v_startLine;
-- 截取終點段線段
select ST_LineLocatePoint(v_endLine, v_endpoint) into v_perEnd;
SELECT ST_LineSubstring(v_endLine,0,v_perEnd) into v_endLine;
-- 將其實線段,終點線段以及路徑進行合並
select st_linemerge(ST_Union(array[v_res,v_startLine,v_endLine])) into v_shPath;


ELSE
select ST_LineLocatePoint(v_res, v_statpoint) into v_perStart;
select ST_LineLocatePoint(v_res, v_endpoint) into v_perEnd;
IF(v_perStart > v_perEnd) THEN
tempnode = v_perStart;
v_perStart = v_perEnd;
v_perEnd = tempnode;
END IF;
SELECT ST_LineSubstring(v_res,v_perStart,v_perEnd ) into v_shPath;
END IF;

-- 截取v_res
return v_shPath;

end;


$function$;

ALTER FUNCTION public.pgr_fromatob(character varying, double precision, double precision, double precision, double precision, text)
OWNER TO postgres;

存儲過程解釋:

1.4326 是坐標系
2.E'\\s ' 是空格的正則表達式
3.pass是途徑點集合,代碼中寫了判斷如果pass有值就計算包括途徑點的路徑分析,如果沒有值只計算起點到終點的路徑。
4.4403000314是我自己路網中的小路的分類編碼,只是做個篩選條件,不是必須的

五、geoserver形成圖層

用geoserver添加數據源將postgresql中的路網表添加到geoserver中,然后創建SQL視圖,然后填入對應sql以及相應參數保存即可

【學習筆記之Openlayers3】路徑分析篇(第六篇) -开发者知识库,保存頁面,第2张

六、效果展示

【學習筆記之Openlayers3】路徑分析篇(第六篇) -开发者知识库,效果圖,第3张

最佳答案:

本文经用户投稿或网站收集转载,如有侵权请联系本站。

发表评论

0条回复