路径分析—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。
实现效果