路径分析—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)、创建数据源

路径分析—PostgreSQL+GeoServer+Openlayers(二)-LMLPHP

 里面的参数主要有:正常的数据源参数、数据库连接参数等

2)、创建图层、编辑 SQL View

新建图层

路径分析—PostgreSQL+GeoServer+Openlayers(二)-LMLPHP

编辑 SQL View,在编辑好查询语句后,参数、返回结果都可以自动读出。

路径分析—PostgreSQL+GeoServer+Openlayers(二)-LMLPHP

 这样一个 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 的使用参考这里。

实现效果

路径分析—PostgreSQL+GeoServer+Openlayers(二)-LMLPHP

10-13 12:00