路徑分析—PostgreSQL+GeoServer+Openlayers(二)

2022-10-13 12:06:30

路徑分析—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 中的函數時,由於不同版本下函數名寫法會有些不一樣,檢視自己所用版本的檔案。

二、GeoServer SQL View 建立

在建立完成資料庫函數後,有兩種方式可以呼叫:

  1、程式碼連線資料庫查詢

  2、GeoServer 中建立圖層,以PostGIS為資料來源,並建立 SQL View

因為原先專案中已經有在用 GeoServer,所用直接選用方式2。

1)、建立資料來源

 

 裡面的引數主要有:正常的資料來源引數、資料庫連線引數等

2)、建立圖層、編輯 SQL View

新建圖層

編輯 SQL View,在編輯好查詢語句後,引數、返回結果都可以自動讀出。

 

 這樣一個 PostGIS 資料來源的圖層就釋出好了

三、Openlayers 呼叫

在 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。

關於TileWMS 和 ImageWMS 的使用參考這裡。

 

實現效果