GeoServer+PostgreSQL+PostGIS+pgRouting实现最短路径查询
一、软件安装
GeoServer下载地址:
http://geoserver.org/download/
PostgreSQL下载地址:
https://www.postgresql.org/download/
paAdmin3下载地址:
https://www.pgadmin.org/download/pgadmin-3-windows/
PostGIS下载地址:
http://postgis.net/windows_downloads/
pgRouting已经包含在安装程序中。
所有下载程序如下:
安装过程不再详述。
二、数据制作
我使用的是ArcMap来绘制路网数据,也可以使用其它的GIS软件,但好像没有这么方便,地理坐标系使用GCS_WGS_1984,编号为:4326。
注意:所有线段连接的地方都需要断开,这样便于以后的分析。
三、数据处理
使用paAdmin3连接PostgreSQL,并执行以下语句,在新的空间数据库里添加空间扩展:
CREATE EXTENSION postgis;
CREATE EXTENSION pgrouting;
CREATE EXTENSION postgis_topology;
CREATE EXTENSION fuzzystrmatch;
CREATE EXTENSION postgis_tiger_geocoder;
CREATE EXTENSION address_standardizer;
制作完路网数据后,需要使用 来把路网数据导入到PostgreSQL中去。
点击“View connection details....”在弹出的窗口中填入PostgreSQL的账号和密码,以及Database。
连接成功后,需要设置一下“Optionns...”
需要使用GBK编码,并勾选最下面一个选项。
添加路网数据,并设置SRID为:4326
导入完成后,会在数据库中创建一个对应的表:
在查询表中分别执行下列SQL,对表结构进行修改:
1.修改表结构
--添加起点id
ALTER TABLE public.road_xblk ADD COLUMN source integer;
--添加终点id
ALTER TABLE public.road_xblk ADD COLUMN target integer;
--添加道路权重值
ALTER TABLE public.road_xblk ADD COLUMN length double precis
2.创建拓扑结构
--为sampledata表创建拓扑布局,即为source和target字段赋值
SELECT pgr_createTopology('public.road_xblk',0.0001, 'geom', 'gid');
3.创建索引
--为source和target字段创建索引
CREATE INDEX source_idx ON road_xblk("source");
CREATE INDEX target_idx ON road_xblk("target");
4.给长度赋值
--为length赋值
update road_xblk set length =st_length(geom);
--为road_xblk表添加reverse_cost字段并用length的值赋值
ALTER TABLE road_xblk ADD COLUMN reverse_cost double precision;
UPDATE road_xblk SET reverse_cost =length;
5.创建最短路径函数
---创建查询随意两点之前的最短路径的函数
1 DROP FUNCTION pgr_fromAtoB(tbl varchar,startx float, starty float,endx float,endy float); 2 3 CREATE OR REPLACE function pgr_fromAtoB(tbl varchar,startx float, starty float,endx float,endy float) 4 5 returns geometry as 6 7 $body$ 8 9 declare 10 11 v_startLine geometry;--离起点最近的线 12 13 v_endLine geometry;--离终点最近的线 14 15 16 17 v_startTarget integer;--距离起点最近线的终点 18 19 v_startSource integer; 20 21 v_endSource integer;--距离终点最近线的起点 22 23 v_endTarget integer; 24 25 26 27 v_statpoint geometry;--在v_startLine上距离起点最近的点 28 29 v_endpoint geometry;--在v_endLine上距离终点最近的点 30 31 32 33 v_res geometry;--最短路径分析结果 34 35 v_res_a geometry; 36 37 v_res_b geometry; 38 39 v_res_c geometry; 40 41 v_res_d geometry; 42 43 44 45 v_perStart float;--v_statpoint在v_res上的百分比 46 47 v_perEnd float;--v_endpoint在v_res上的百分比 48 49 50 51 v_shPath_se geometry;--开始到结束 52 53 v_shPath_es geometry;--结束到开始 54 55 v_shPath geometry;--最终结果 56 57 tempnode float; 58 59 begin 60 61 --查询离起点最近的线 62 63 execute 'select geom, source, target from ' ||tbl|| 64 65 ' where ST_DWithin(geom,ST_Geometryfromtext(''point('|| startx ||' ' || starty||')'',4326),15) 66 67 order by ST_Distance(geom,ST_GeometryFromText(''point('|| startx ||' '|| starty ||')'',4326)) limit 1' 68 69 into v_startLine, v_startSource ,v_startTarget; 70 71 72 73 --查询离终点最近的线 74 75 execute 'select geom, source, target from ' ||tbl|| 76 77 ' where ST_DWithin(geom,ST_Geometryfromtext(''point('|| endx || ' ' || endy ||')'',4326),15) 78 79 order by ST_Distance(geom,ST_GeometryFromText(''point('|| endx ||' ' || endy ||')'',4326)) limit 1' 80 81 into v_endLine, v_endSource,v_endTarget; 82 83 84 85 --如果没找到最近的线,就返回null 86 87 if (v_startLine is null) or (v_endLine is null) then 88 89 return null; 90 91 end if ; 92 93 94 95 select ST_ClosestPoint(v_startLine, ST_Geometryfromtext('point('|| startx ||' ' || starty ||')',4326)) into v_statpoint; 96 97 select ST_ClosestPoint(v_endLine, ST_GeometryFromText('point('|| endx ||' ' || endy ||')',4326)) into v_endpoint; 98 99 100 101 -- ST_Distance 102 103 104 105 --从开始的起点到结束的起点最短路径 106 107 execute 'SELECT st_linemerge(st_union(b.geom)) ' || 108 109 'FROM pgr_kdijkstraPath( 110 111 ''SELECT gid as id, source, target, length as cost FROM ' || tbl ||''',' 112 113 ||v_startSource || ', ' ||'array['||v_endSource||'] , false, false 114 115 ) a, ' 116 117 || tbl || ' b 118 119 WHERE a.id3=b.gid 120 121 GROUP by id1 122 123 ORDER by id1' into v_res ; 124 125 126 127 --从开始的终点到结束的起点最短路径 128 129 execute 'SELECT st_linemerge(st_union(b.geom)) ' || 130 131 'FROM pgr_kdijkstraPath( 132 133 ''SELECT gid as id, source, target, length as cost FROM ' || tbl ||''',' 134 135 ||v_startTarget || ', ' ||'array['||v_endSource||'] , false, false 136 137 ) a, ' 138 139 || tbl || ' b 140 141 WHERE a.id3=b.gid 142 143 GROUP by id1 144 145 ORDER by id1' into v_res_b ; 146 147 148 149 --从开始的起点到结束的终点最短路径 150 151 execute 'SELECT st_linemerge(st_union(b.geom)) ' || 152 153 'FROM pgr_kdijkstraPath( 154 155 ''SELECT gid as id, source, target, length as cost FROM ' || tbl ||''',' 156 157 ||v_startSource || ', ' ||'array['||v_endTarget||'] , false, false 158 159 ) a, ' 160 161 || tbl || ' b 162 163 WHERE a.id3=b.gid 164 165 GROUP by id1 166 167 ORDER by id1' into v_res_c ; 168 169 170 171 --从开始的终点到结束的终点最短路径 172 173 execute 'SELECT st_linemerge(st_union(b.geom)) ' || 174 175 'FROM pgr_kdijkstraPath( 176 177 ''SELECT gid as id, source, target, length as cost FROM ' || tbl ||''',' 178 179 ||v_startTarget || ', ' ||'array['||v_endTarget||'] , false, false 180 181 ) a, ' 182 183 || tbl || ' b 184 185 WHERE a.id3=b.gid 186 187 GROUP by id1 188 189 ORDER by id1' into v_res_d ; 190 191 192 193 if(ST_Length(v_res) > ST_Length(v_res_b)) then 194 195 v_res = v_res_b; 196 197 end if; 198 199 200 201 if(ST_Length(v_res) > ST_Length(v_res_c)) then 202 203 v_res = v_res_c; 204 205 end if; 206 207 208 209 if(ST_Length(v_res) > ST_Length(v_res_d)) then 210 211 v_res = v_res_d; 212 213 end if; 214 215 216 217 218 219 --如果找不到最短路径,就返回null 220 221 --if(v_res is null) then 222 223 -- return null; 224 225 --end if; 226 227 228 229 --将v_res,v_startLine,v_endLine进行拼接 230 231 select st_linemerge(ST_Union(array[v_res,v_startLine,v_endLine])) into v_res; 232 233 234 235 select ST_Line_Locate_Point(v_res, v_statpoint) into v_perStart; 236 237 select ST_Line_Locate_Point(v_res, v_endpoint) into v_perEnd; 238 239 240 241 if(v_perStart > v_perEnd) then 242 243 tempnode = v_perStart; 244 245 v_perStart = v_perEnd; 246 247 v_perEnd = tempnode; 248 249 end if; 250 251 252 253 --截取v_res 254 255 SELECT ST_Line_SubString(v_res,v_perStart, v_perEnd) into v_shPath; 256 257 258 259 return v_shPath; 260 261 262 263 end; 264 265 $body$ 266 267 LANGUAGE plpgsql VOLATILE STRICT;
四、数据发布
数据准备完成后,就需要用GeoServer来进行发布:
启动GeoServer,在浏览器中输入,http://localhost:8080/geoserver/web/,登录到GeoServer。
1.创建工作区
添加xblk名称的工作区。
2.添加数据存储:
填入对应的连接信息:
3.添加图层:
注意红框中的内容。
4.添加路径查询服务,添加图层,选择“配置新的SQL视图”:
视图名称:navigation
SQL语句:
SELECT * FROM pgr_fromAtoB('road_xblk', %x1%, %y1%, %x2%, %y2%)
验证的正则表达式:^-?[\d.]+$
类型:LingString
SRID:4326
点击保存后,填入SRS,并自动计算范围:
五、结果展示
使用OpenLayer进行结果展示,代码请直接下载,结果如下:
OpenLayer展示代码下载
说明:这个最短路径的算法有一定的问题,在特定的条件下,查找的不一定是最短的路径,需要对数据进行再处理,或者对算法进行优化。