【学习笔记之Openlayers3】路径分析篇(第六篇)

时间:2023-02-01 09:15:33

最佳路径分析

最近在使用openlayer3进行项目开发的时候,由于项目需求要实现最佳路径分析功能。原先没做过,找了网上的资料,然后现在将自己做的总结一下。


相关算法

首先找了相关的路径分析算法一种是迪杰克斯特拉(Dijkstra)算法
还有一种是Floyd-Warshall算法 ,目前比较常用的是迪杰克斯特拉(Dijkstra)算法进行路径分析的。

如何实现路径分析

首先开发环境是:openlayers3+geoserver+postgresql;
实现步骤:

1.首先要有一张路网的表
2.postgresql添加相应扩展
3.形成拓扑关系
4.编写相应的最短路径存储过程
5.在geoserver中调用该存储过程形成最短路径图层

步骤详解

一、路网表

    首先你要有路网表才能进行路径分析,这个就不多赘述了

二、 添加扩展

 用postgresql的"SQL Shell"向具体要使用的数据库添加扩展 
CREATE EXTENSION postgis;
CREATE EXTENSION pgrouting;
CREATE EXTENSION postgis_topology;
CREATE EXTENSION fuzzystrmatch;
CREATE EXTENSION postgis_tiger_geocoder;
CREATE EXTENSION address_standardizer;

三、 形成拓扑关系

1.创建拓扑结构

-- 添加起点id

ALTER TABLE public.tablename ADD COLUMN source integer;

-- 添加终点id

ALTER TABLE public.tablename ADD COLUMN target integer;

-- 添加道路权重值

ALTER TABLE public.tablename ADD COLUMN length double precision;
-- 创建拓扑结构 0.00001 这个可以自己定义这个数值越大越模糊,越小越精细,这个看实际需求吧
SELECT pgr_createTopology('public.tablename ',0.00001, 'geom', 'gid');
-- 创建索引
CREATE INDEX source_idx ON tablename("source");

CREATE INDEX target_idx ON tablename("target");
-- 为length赋值 这个将会作为路径分析中的权值
update tablename set length =st_length(geom);

2.为路网表添加字段:添加字段为了存储路网表中每条记录的道路的起点经纬度以及终点经纬度

ALTER TABLE tablename ADD COLUMN x1 double precision;

ALTER TABLE tablename ADD COLUMN y1 double precision;

ALTER TABLE tablename ADD COLUMN x2 double precision;

ALTER TABLE tablename ADD COLUMN y2 double precision;
-- 为新建的字段赋值
UPDATE tablename SET x1 =ST_x(ST_PointN(geom, 1));

UPDATE tablename SET y1 =ST_y(ST_PointN(geom, 1));

UPDATE tablename SET x2 =ST_x(ST_PointN(geom, ST_NumPoints(geom)));

UPDATE tablename SET y2 =ST_y(ST_PointN(geom, ST_NumPoints(geom)));

四、存储过程

-- FUNCTION: public.pgr_fromatob(character varying, double precision, double precision, double precision, double precision, text)

-- DROP FUNCTION public.pgr_fromatob(character varying, double precision, double precision, double precision, double precision, text);

CREATE OR REPLACE FUNCTION public.pgr_fromatob(
tbl character varying,
startx double precision,
starty double precision,
endx double precision,
endy double precision,
pass text)
RETURNS geometry
LANGUAGE 'plpgsql'
COST 100.0
VOLATILE NOT LEAKPROOF STRICT
AS $function$



declare
v_startLine geometry;-- 离起点最近的线
v_endLine geometry;-- 离终点最近的线

v_startTarget integer;-- 距离起点 最近线的终点
v_startSource integer;-- 距离起点 最近线的起点
v_endTarget integer;-- 距离终点 最近线的终点
v_endSource integer;-- 距离终点 最近线的起点

v_statpoint geometry;-- 在v_startLine上距离起点最近的点
v_endpoint geometry;-- 在v_endLine上距离终点最近的点

v_passarray text[]; -- 途径点point数组
v_passids integer[]; -- 途径点target id
v_passSources integer[]; -- 途径点source id
v_passTarget integer; -- 途径线段的终点
v_passSource integer; -- 途径线的起点
v_passLine geometry; -- 距离途径点最近的线
v_passLines geometry[]; -- 所有途径点的线数组

v_res geometry;-- 最短路径分析结果
v_passres geometry; -- 途径点路径分析结果
v_ress geometry[]; -- 包含途径点最短路径的分析结果

v_perStart float;-- v_statpoint在v_res上的百分比
v_perEnd float;-- v_endpoint在v_res上的百分比

v_shPath geometry;-- 最终结果
tempnode float;
passp varchar;

v_tempTarget int;

begin

-- 查询离起点最近的线
execute 'select geom ,target,source from ' ||tbl||
' where
ST_DWithin(geom,ST_Geometryfromtext(''point('|| startx ||' ' || starty||')'',4326),0.1)
order by ST_Distance(geom,ST_GeometryFromText(''point('|| startx ||' '|| starty ||')'',4326)) limit 1'
into v_startLine ,v_startTarget,v_startSource;

-- 查询离终点最近的线
execute 'select geom,target,source from ' ||tbl||
' where ST_DWithin(geom,ST_Geometryfromtext(''point('|| endx || ' ' || endy ||')'',4326),0.1)
order by ST_Distance(geom,ST_GeometryFromText(''point('|| endx ||' ' || endy ||')'',4326)) limit 1'
into v_endLine,v_endTarget,v_endSource;

-- 如果没找到最近的线,就返回null
IF (v_startLine is null) or (v_endLine is null) THEN
return null;
END IF ;

-- 判断是否存在途径点
IF (pass !='') THEN
-- 对参数进行处理形成array 参数样例 'x1,y1 x2,y2 '
select regexp_split_to_array(pass, E'\\s+') into v_passarray;

FOR i IN array_lower(v_passarray, 1) .. array_upper(v_passarray, 1)
LOOP
passp :=v_passarray[i];

IF (passp !='') THEN
execute 'select geom,target,source from ' ||tbl||
' where
ST_DWithin(geom,ST_Geometryfromtext(''point('|| split_part(passp,',',1) ||' ' || split_part(passp,',',2)||')'',4326),0.1)
order by ST_Distance(geom,ST_GeometryFromText(''point('|| split_part(passp,',',1) ||' '|| split_part(passp,',',2) ||')'',4326)) limit 1'
into v_passLine,v_passTarget,v_passSource;

select array_append(v_passids,v_passTarget) into v_passids;
select array_append(v_passSources,v_passSource) into v_passSources;
select array_append(v_passLines,v_passLine) into v_passLines;

end if ;

END Loop;
end if ;


select array_append(v_passSources,v_endSource) into v_passSources; -- 将source传入source
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;


IF (pass !='') THEN -- 判断是否包含途径点
-- 将途径点进行循环计算每个途径点间的最短路径
-- 4403000314 是小路的编码,分析中除去小路的影响
FOR i IN array_lower(v_passSources, 1) .. array_upper(v_passSources, 1)
LOOP
IF i=1 THEN
EXECUTE 'SELECT st_linemerge(st_union(b.geom)) ' ||
'FROM pgr_kdijkstraPath(
''SELECT gid as id, source, target, length as cost FROM public.' || quote_ident(tbl) ||' where fcode !='''''||'4403000314'||''''''','
||v_startSource || ', ' ||'array['||v_passSources[i]||'] , false, false
) a, '
|| quote_ident(tbl) || ' b
WHERE a.id3=b.gid
GROUP by id1
ORDER by id1' into v_passres;
ELSE
-- 判断当前的途径点后有没有可以连接的线(即判断这个途径点所在线段是否为'死胡同')
EXECUTE 'select * from hangzhouload_1 t where t.source= '||v_passids[i-1]||'' into v_tempTarget;
IF (v_tempTarget is null) THEN
-- 如果为'死胡同'则原路返回,以该条线段起始点为节点进行路径分析
v_tempTarget:=v_passSources[i-1];
ELSE
v_tempTarget:=v_passids[i-1];
END IF;
EXECUTE 'SELECT st_linemerge(st_union(b.geom)) ' ||
'FROM pgr_kdijkstraPath(
''SELECT gid as id, source, target, length as cost FROM ' || tbl ||' where fcode !='''''||'4403000314'||''''''','
||v_tempTarget || ', ' ||'array['||v_passSources[i]||'] , false, false
) a, '
|| tbl || ' b
WHERE a.id3=b.gid
GROUP by id1
ORDER by id1' into v_passres;
END IF;
-- 将分析出的路段以及途径点所在(附近的路径)添加到数组中
select array_append(v_ress,v_passres) into v_ress;
-- 排除最后一条线段(终点所在线段/附近线段)
IF(i<array_upper(v_passSources, 1)) THEN
select array_append(v_ress,v_passLines[i]) into v_ress;
END IF;
END Loop;
-- 如果排除小路找不到最佳路径,则放开对小路的限制
IF (v_ress is null) THEN

FOR i IN array_lower(v_passSources, 1) .. array_upper(v_passSources, 1)
LOOP
IF i=1 THEN
EXECUTE 'SELECT st_linemerge(st_union(b.geom)) ' ||
'FROM pgr_kdijkstraPath(
''SELECT gid as id, source, target, length as cost FROM public.' || quote_ident(tbl) ||''','
||v_startTarget || ', ' ||'array['||v_passSources[i]||'] , false, false
) a, '
|| quote_ident(tbl) || ' b
WHERE a.id3=b.gid
GROUP by id1
ORDER by id1' into v_passres;
ELSE
EXECUTE 'SELECT st_linemerge(st_union(b.geom)) ' ||
'FROM pgr_kdijkstraPath(
''SELECT gid as id, source, target, length as cost FROM ' || tbl ||''','
||v_passids[i-1] || ', ' ||'array['||v_passSources[i]||'] , false, false
) a, '
|| tbl || ' b
WHERE a.id3=b.gid
GROUP by id1
ORDER by id1' into v_passres;
END IF;
select array_append(v_ress,v_passres) into v_ress;
END LOOP;

END IF;
-- 处理线段

select st_linemerge(ST_Union(array[v_ress])) into v_res;

ELSE

EXECUTE 'SELECT st_linemerge(st_union(b.geom)) ' ||
'FROM pgr_kdijkstraPath(
''SELECT gid as id, source, target, length as cost FROM ' || tbl ||' where fcode !='''''||'4403000314'||''''''','
||v_startTarget || ', ' ||'array['||v_endSource||'] , false, false
) a, '
|| tbl || ' b
WHERE a.id3=b.gid
GROUP by id1
ORDER by id1' into v_res ;
-- 如果找不到最短路径,就返回null
IF(v_res is null) THEN
EXECUTE 'SELECT st_linemerge(st_union(b.geom)) ' ||
'FROM pgr_kdijkstraPath(
''SELECT gid as id, source, target, length as cost FROM ' || tbl ||''','
||v_startTarget || ', ' ||'array['||v_endSource||'] , false, false
) a, '
|| tbl || ' b
WHERE a.id3=b.gid
GROUP by id1
ORDER by id1' into v_res ;
END IF;
-- 将v_res,v_startLine,v_endLine进行拼接
select st_linemerge(ST_Union(array[v_res,v_startLine,v_endLine])) into v_res;
END IF;


-- 最短路径
-- 判断当前路径线类型
IF(GeometryType(v_res)='MULTILINESTRING') THEN
-- 截取起始段线段
select ST_LineLocatePoint(v_startLine, v_statpoint) into v_perStart;

select ST_LineSubstring(v_startLine,0,v_perStart ) into v_startLine;
-- 截取终点段线段
select ST_LineLocatePoint(v_endLine, v_endpoint) into v_perEnd;
SELECT ST_LineSubstring(v_endLine,0,v_perEnd) into v_endLine;
-- 将其实线段,终点线段以及路径进行合并
select st_linemerge(ST_Union(array[v_res,v_startLine,v_endLine])) into v_shPath;


ELSE
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;
SELECT ST_LineSubstring(v_res,v_perStart,v_perEnd ) into v_shPath;
END IF;

-- 截取v_res
return v_shPath;

end;


$function$;

ALTER FUNCTION public.pgr_fromatob(character varying, double precision, double precision, double precision, double precision, text)
OWNER TO postgres;

存储过程解释:

1.4326 是坐标系
2.E'\\s+' 是空格的正则表达式
3.pass是途径点集合,代码中写了判断如果pass有值就计算包括途径点的路径分析,如果没有值只计算起点到终点的路径。
4.4403000314是我自己路网中的小路的分类编码,只是做个筛选条件,不是必须的

五、geoserver形成图层

用geoserver添加数据源将postgresql中的路网表添加到geoserver中,然后创建SQL视图,然后填入对应sql以及相应参数保存即可

【学习笔记之Openlayers3】路径分析篇(第六篇)

六、效果展示

【学习笔记之Openlayers3】路径分析篇(第六篇)