PostGis+GeoServer+OpenLayers最短路径分析

使用PostGis存储路线数据,在GeoServer中发布路网图层和最短路径图层,通过OpenLayers建立路网和展示图层。

1、PostGis:PostgreSQL 12.4,
2、GeoServer:geoserver-2.17.2
3、OpenLayers:6.4.3

一、做成路网数据

1、在PostGis建立路网数据表,主键使用sequence,路网geom 使用geometry(LineString,4326)

CREATE SEQUENCE public.my_roads_id_seq
    INCREMENT 1
    START 1
    MINVALUE 1
    MAXVALUE 2147483647
    CACHE 1;
    
ALTER SEQUENCE public.my_roads_id_seq OWNER TO postgres;

CREATE TABLE public.my_roads
(
    id integer NOT NULL DEFAULT nextval('my_roads_id_seq'::regclass),
    name character varying(32) COLLATE pg_catalog."default",
    geom geometry(LineString,4326),
    CONSTRAINT my_roads_pkey PRIMARY KEY (id)
)TABLESPACE pg_default;

ALTER TABLE public.my_roads OWNER to postgres;

2、在GeoServer中发布路网图层

建立工作区,建立postgis的数据存储,发布my_roads图层。
在这里插入图片描述
EPSG使用4326,边框及经纬度为:【-180,-90 】【180,90】即可。
在这里插入图片描述

3、OpenLayers中建立路网

使用vue开发路网编辑画面。
在这里插入图片描述
实现方式,请参考:OpenLayers 在Vue中增删改

注意点:新增的线不再是newFeature.setGeometry(new MultiLineString([geometry.getCoordinates()]))

// 转换坐标
 let geometry = this.drewFeature.getGeometry().clone();
 geometry.applyTransform(function (flatCoordinates, flatCoordinates2, stride) {
     for (let j = 0; j < flatCoordinates.length; j += stride) {
         let y = flatCoordinates[j];
         let x = flatCoordinates[j + 1];
         flatCoordinates[j] = x;
         flatCoordinates[j + 1] = y;
     }
 });

 // 设置feature对应的属性,这些属性是根据数据源的字段来设置的
 let newFeature = new Feature();
 newFeature.setId('nyc_roads.new.' + this.newId);
 newFeature.setGeometryName('the_geom');
 newFeature.set('the_geom', null);
 newFeature.set('name', newFeature.getId());
 newFeature.setGeometry(new MultiLineString([geometry.getCoordinates()]));

而是newFeature.setGeometry(geometry);

// 转换坐标
let geometry = this.drewFeature.getGeometry().clone();
geometry.applyTransform(function (flatCoordinates, flatCoordinates2, stride) {
    for (let j = 0; j < flatCoordinates.length; j += stride) {
        let y = flatCoordinates[j];
        let x = flatCoordinates[j + 1];
        flatCoordinates[j] = x;
        flatCoordinates[j + 1] = y;
    }
});

// 设置feature对应的属性,这些属性是根据数据源的字段来设置的
let newFeature = new Feature();
newFeature.setId('am_roads.new.' + this.newId);
newFeature.setGeometryName('geom');
newFeature.set('geom', null);
newFeature.set('name', newFeature.getId());
newFeature.setGeometry(geometry);

编辑好的路网如下:
在这里插入图片描述

二、路径分析基础数据

1、路网拓扑生成

-- 创建拓扑布局,即为source和target字段赋值
select pgr_createTopology('my_roads',0.001, 'geom', 'id');

--为cost 赋值
update my_roads set cost =st_length(geom);
--为双向reverse_cost 赋值
UPDATE my_roads SET reverse_cost = st_length(geom);

--为线的起点和终点坐标赋值 
UPDATE my_roads SET x1 =ST_x(ST_PointN(geom, 1));	
UPDATE my_roads SET y1 =ST_y(ST_PointN(geom, 1));	
UPDATE my_roads SET x2 =ST_x(ST_PointN(geom, ST_NumPoints(geom)));	
UPDATE my_roads SET y2 =ST_y(ST_PointN(geom, ST_NumPoints(geom)));

2、最短路径函数

此函数参考网上路径分析函数,解决了计算不准确问题和MultiLineString问题。

  • 建立根据拓扑后节点,计算最短路径函数pgr_shortestpath_edge。
CREATE  OR REPLACE FUNCTION pgr_shortestpath_edge (
	tbl CHARACTER VARYING,
	fromNode INTEGER,
	toNode INTEGER,
	startLine geometry,
	endLine geometry)
RETURNS geometry
AS $BODY$ 
DECLARE
	v_res geometry;--最短路径分析结果
	v_linemerge geometry;--合并线后结果
	v_shPath geometry;--最终结果
BEGIN
	--从开始的节点到结束的节点最短路径
	EXECUTE'SELECT st_linemerge(st_union(b.geom)) ' || 'FROM 
	pgr_dijkstra( ''SELECT id, source, target, cost FROM ' || tbl || ''',' || fromNode || ', ' || toNode || ', false ) a
	LEFT JOIN  ' || tbl || ' b  ON a.edge = b.id ' INTO v_res;
	
	-- 合并线
	select  st_linemerge(ST_Union(array[startLine, v_res, endLine])) into v_linemerge; 
	
 	RETURN v_linemerge;
END; 

$BODY$ LANGUAGE plpgsql VOLATILE STRICT COST 100;
ALTER FUNCTION pgr_shortestpath_edge ( CHARACTER VARYING, INTEGER, INTEGER, geometry, geometry ) OWNER TO postgres;
  • 建立最短路径函数
CREATE  OR REPLACE FUNCTION pgr_shortestpath ( tbl CHARACTER VARYING, startx DOUBLE PRECISION, starty DOUBLE PRECISION, endx DOUBLE PRECISION, endy DOUBLE PRECISION ) RETURNS geometry
AS $BODY$ DECLARE
	v_startLine geometry;--离起点最近的线
	v_endLine geometry;--离终点最近的线
	v_startPoint geometry;--在v_startLine上距离起点最近的点
	v_endPoint geometry;--在v_endLine上距离终点最近的点
	
	v_startLine_pre geometry; --起点到v_startPoint的线  
	v_endLine_next geometry; --终点到v_endPoint的线  
	v_startPoint_pre geometry;  --起点
	v_endPoint_pre geometry;  --终点
	
	v_startTarget INTEGER;--距离起点最近线的目标节点
	v_startSource INTEGER;--距离起点最近线的源节点
	v_endSource INTEGER;--距离终点最近线的源节点
	v_endTarget INTEGER;--距离起点最近线的目标节点
	v_start_x1 double precision;
	v_start_y1 double precision;
	v_start_x2 double precision;
	v_start_y2 double precision;
	v_end_x1 double precision;
	v_end_y1 double precision;
	v_end_x2 double precision;
	v_end_y2 double precision;
	
	v_startLine_Source geometry;
	v_startLine_Target geometry;
	v_endLine_Source geometry;
	v_endLine_Target geometry;
	
	v_res geometry;--最短路径分析结果
	v_res_a geometry;
	v_res_b geometry;
	v_res_c geometry;
	v_res_d geometry;
	
	v_shPath geometry;--最终结果

BEGIN
	--查询离0.01度(大约1000米)范围内起点的最近线
	EXECUTE'select geom, source, target,x1,y1,x2,y2  from ' || tbl || ' where ST_DWithin(geom,ST_Geometryfromtext(''point(' || startx || ' ' || starty || ')'',4326),0.01)
	order by ST_Distance(geom,ST_GeometryFromText(''point(' || startx || ' ' || starty || ')'',4326))  limit 1'
	INTO v_startLine,v_startSource,v_startTarget,v_start_x1,v_start_y1,v_start_x2,v_start_y2;
	
	--查询离终点0.01度(大约1000米)范围内的最近线
	EXECUTE'select geom, source, target,x1,y1,x2,y2 from ' || tbl || ' where ST_DWithin(geom,ST_Geometryfromtext(''point(' || endx || ' ' || endy || ')'',4326),0.01)
	order by ST_Distance(geom,ST_GeometryFromText(''point(' || endx || ' ' || endy || ')'',4326))  limit 1' 
	INTO v_endLine,v_endSource,v_endTarget,v_end_x1,v_end_y1,v_end_x2,v_end_y2;
	
	--如果没找到最近的线,就返回null
	IF ( v_startLine IS NULL )  OR ( v_endLine IS NULL ) THEN
		RETURN NULL;
	END IF;
	
	 --创建查询的起点和终点  
	 SELECT ST_SetSRID( ST_MakePoint(startx , starty),4326 )INTO v_startPoint_pre;  
	 SELECT ST_SetSRID( ST_MakePoint(endx , endy),4326 )INTO v_endPoint_pre;  
	 
	--查询线上最接近某点的点(此点在线上)
	SELECT ST_ClosestPoint ( v_startLine, v_startPoint_pre) INTO v_startPoint;
	SELECT ST_ClosestPoint ( v_endLine, v_endPoint_pre) INTO v_endPoint;

	--根据最近点,把最近线分为source和target两段
	SELECT ST_MakeLine( v_startPoint,ST_SetSRID( ST_MakePoint(v_start_x1 , v_start_y1),4326 )) INTO v_startLine_Source;
	SELECT ST_MakeLine( v_startPoint,ST_SetSRID( ST_MakePoint(v_start_x2 , v_start_y2),4326 )) INTO v_startLine_Target;
	SELECT ST_MakeLine( v_endPoint,ST_SetSRID( ST_MakePoint(v_end_x1 , v_end_y1),4326 )) INTO v_endLine_Source;
	SELECT ST_MakeLine( v_endPoint,ST_SetSRID( ST_MakePoint(v_end_x2 , v_end_y2),4326 )) INTO v_endLine_Target;
	
	--计算最短路径(开始的起点/终点 --- 结束的起点/终点)
	SELECT pgr_shortestpath_edge (tbl, v_startSource, v_endSource, v_startLine_Source, v_endLine_Source) INTO v_res_a;
	SELECT pgr_shortestpath_edge (tbl, v_startTarget, v_endSource, v_startLine_Target, v_endLine_Source) INTO v_res_b;
	SELECT pgr_shortestpath_edge (tbl, v_startSource, v_endTarget, v_startLine_Source, v_endLine_Target) INTO v_res_c;
	SELECT pgr_shortestpath_edge (tbl, v_startTarget, v_endTarget, v_startLine_Target, v_endLine_Target) INTO v_res_d;
	
	v_res = v_res_a;
	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;
	
	--创建查询起点和终点到最近的线之间的线
	SELECT ST_MakeLine( v_startPoint,v_startPoint_pre) INTO v_startLine_pre;  
	SELECT ST_MakeLine( v_endPoint,v_endPoint_pre) INTO v_endLine_next;  

	select st_union(array[v_endLine_next, v_res, v_startLine_pre]) into v_shPath; 
	
    return v_shPath;
END; 

$BODY$ LANGUAGE plpgsql VOLATILE STRICT COST 100;
ALTER FUNCTION pgr_shortestpath ( CHARACTER VARYING, DOUBLE PRECISION, DOUBLE PRECISION, DOUBLE PRECISION, DOUBLE PRECISION ) OWNER TO postgres;

三、显示最短路径

1、GeoServer建立创建sql视图

在这里插入图片描述
在这里插入图片描述
D的默认值全部为0,
验证的正则表达式全部为^-?[\d.]+$
类型选为 Geometry类型,SRD为4326
在这里插入图片描述

2、OpenLayer中传参并加载最短路径图层

VUE中增加加载最短路径图层方法:

querypath(){
    let startCoord = [116.43442149206543,39.927703206481944]
    let destCoord = [116.410648020996,39.9069321798706]
    let params = {
        LAYERS: 'postgis:shortestpath',
        FORMAT: 'image/png',
    };
    let viewparams = [
        'x1:' + startCoord[0], 'y1:' + startCoord[1],
        'x2:' + destCoord[0], 'y2:' + destCoord[1]
    ];
    params.viewparams = viewparams .join(';');

    let imageLayer = new ImageLayer({
        source: new ImageWMS(
            {
                url: 'http://localhost:8088/geoserver/postgis/wms',
                params: params
            }),
    });
    this.map.addLayer(imageLayer);
},

3、最终结果显示

在这里插入图片描述

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值