路徑分析—QGIS+PostgreSQL+PostGIS+pgRouting(一)
路徑分析—PostgreSQL+GeoServer+Openlayers(二)
上一篇文章中實現資料庫層面的路徑分析了,可以在資料庫裡面通過 SQL 查詢到結果。
本篇文章實現了從前端頁面直接視覺化操作點選起點、終點,並返回最短路徑進行展示。
在 PostgreSQL 資料庫中建立函數,該函數實現的功能是:傳入表名、起點、終點經緯度、距離等引數,返回對應的最短距離 geometry。
建立的函數具體如下:
-- 刪除已經存在的函數(可能會報錯,報錯的話註釋) DROP FUNCTION pgr_fromAtoB(tbl varchar,startx float, starty float,endx float,endy float); -- tbl路網表名 -- startx起點經度 -- starty起點緯度 -- endx終點經度 -- endy終點緯度 -- diatance 起點、終點到路徑查詢的距離 CREATE OR REPLACE function pgr_fromAtoB(tbl varchar,startx float, starty float,endx float,endy float, distance float) --限制返回型別 returns geometry as $body$ declare v_startLine geometry; -- 離起點最近的線 v_endLine geometry; -- 離終點最近的線 v_startSource integer; -- 距離起點最近線的起點 v_startTarget integer; -- 距離起點最近線的終點 v_endSource integer; -- 距離終點最近線的起點 v_endTarget integer; -- 距離終點最近線的終點 v_statpoint geometry; -- 在v_startLine上距離起點(傳入的起點)最近的點 v_endpoint geometry; -- 在v_endLine上距離終點(傳入的終點)最近的點 v_res geometry; -- 最短路徑分析結果 v_res_a geometry; v_res_b geometry; v_res_c geometry; v_res_d geometry; v_perStart float; -- v_statpoint在v_res上的百分比 v_perEnd float; -- v_endpoint在v_res上的百分比 v_shPath_ss geometry; --起點到最近距離點的線 v_shPath_ee geometry; --終點到最近距離點的線 v_shPath geometry; --最終結果 tempnode float; begin -- 4326座標系 -- 查詢離起點最近的線 -- 找起點15米範圍內的最近線 execute 'select geom, source, target from ' ||tbl|| ' where ST_DWithin(geom,ST_Geometryfromtext(''point('|| startx || ' ' || starty||')'',4326),'|| distance ||') order by ST_Distance(geom,ST_GeometryFromText(''point('|| startx ||' '|| starty ||')'',4326)) limit 1' into v_startLine, v_startSource ,v_startTarget; -- 查詢離終點最近的線 -- 找終點15米範圍內的最近線 execute 'select geom, source, target from ' ||tbl|| ' where ST_DWithin(geom,ST_Geometryfromtext(''point('|| endx || ' ' || endy ||')'',4326),'|| distance ||') order by ST_Distance(geom,ST_GeometryFromText(''point('|| endx ||' ' || endy ||')'',4326)) limit 1' into v_endLine, v_endSource,v_endTarget; -- 如果沒找到最近的線,就返回null if (v_startLine is null) or (v_endLine is null) then return null; end if ; -- 分別找到路徑上距離起點和終點最近的點 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; -- 從開始的起點到結束的起點最短路徑 execute 'SELECT ST_Union(b.geom) ' || 'FROM pgr_dijkstra( ''SELECT id, source, target, length as cost FROM ' || tbl ||''',' || v_startSource || ', ' || v_endSource || ' , false ) a LEFT JOIN ' || tbl || ' b ON a.edge=b.id' into v_res ; --從開始的終點到結束的起點最短路徑 execute 'SELECT ST_Union(b.geom) ' || 'FROM pgr_dijkstra( ''SELECT id, source, target, length as cost FROM ' || tbl ||''',' || v_startTarget || ', ' || v_endSource || ' , false ) a LEFT JOIN ' || tbl || ' b ON a.edge=b.id' into v_res_b ; --從開始的起點到結束的終點最短路徑 execute 'SELECT ST_Union(b.geom) ' || 'FROM pgr_dijkstra( ''SELECT id, source, target, length as cost FROM ' || tbl ||''',' || v_startSource || ', ' || v_endTarget || ' , false ) a LEFT JOIN ' || tbl || ' b ON a.edge=b.id' into v_res_c ; --從開始的終點到結束的終點最短路徑 execute 'SELECT ST_Union(b.geom) ' || 'FROM pgr_dijkstra( ''SELECT id, source, target, length as cost FROM ' || tbl ||''',' || v_startTarget || ', ' || v_endTarget || ' , false ) a LEFT JOIN ' || tbl || ' b ON a.edge=b.id' into v_res_d ; if(ST_Length(v_res) > ST_Length(v_res_b)) then v_res = v_res_b; end if; if(ST_Length(v_res) > ST_Length(v_res_c)) then v_res = v_res_c; end if; if(ST_Length(v_res) > ST_Length(v_res_d)) then v_res = v_res_d; end if; -- 如果找不到最短路徑,就返回null (根據實際情況是否需要) -- if(v_res is null) then -- return null; -- end if; --將 v_res,v_startLine,v_endLine 進行拼接 select ST_LineMerge(ST_Union(array[v_res,v_startLine,v_endLine])) into v_res; -- 根據起點、終點最近距離點,找到在路徑中的百分比 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; --擷取 v_res SELECT ST_LineSubstring(v_res,v_perStart, v_perEnd) into v_shPath; SELECT ST_MakeLine(ST_SetSRID(ST_Point( startx, starty),4326),v_statpoint) into v_shPath_ss; SELECT ST_MakeLine(ST_SetSRID(ST_Point( endx, endy),4326),v_endpoint) into v_shPath_ee; -- 將 v_shPath、v_shPath_ss、v_shPath_ee 拼接 select ST_LineMerge(ST_Union(array[v_shPath,v_shPath_ss,v_shPath_ee])) into v_shPath; return v_shPath; end; $body$ LANGUAGE plpgsql VOLATILE STRICT;
注意:
在使用 PostGIS 中的函數時,由於不同版本下函數名寫法會有些不一樣,檢視自己所用版本的檔案。
在建立完成資料庫函數後,有兩種方式可以呼叫:
1、程式碼連線資料庫查詢
2、GeoServer 中建立圖層,以PostGIS為資料來源,並建立 SQL View
因為原先專案中已經有在用 GeoServer,所用直接選用方式2。
1)、建立資料來源
裡面的引數主要有:正常的資料來源引數、資料庫連線引數等
2)、建立圖層、編輯 SQL View
新建圖層
編輯 SQL View,在編輯好查詢語句後,引數、返回結果都可以自動讀出。
這樣一個 PostGIS 資料來源的圖層就釋出好了
在 openlayers 中呼叫,主要就是WMS圖層的呼叫,這裡主要是引數的傳遞。
下面就只貼出呼叫 WMS 圖層,關於其他起始點點選、清空、分析等具體互動就不在這裡。
const params = { LAYERS: 'layername', VERSION: '1.1.0', REQUEST: 'GetMap', FORMAT: 'image/png' } // pathPoint 起點、終點座標 const viewparams = [`x1:${this.pathPoint[0][0]}`, `y1:${this.pathPoint[0][1]}`, `x2:${this.pathPoint[1][0]}`, `y2:${this.pathPoint[1][1]}`] params.viewparams = viewparams.join(';') this.pathLayer = new Image({ source: new ImageWMS({ url: `${GEOSERVER_URL}/wms`, params }) }) this.map.addLayer(this.pathLayer)
這裡用的是 ImageWMS。
實現效果