在PostgreSQL中使用pgRouting
半野

1.环境

1.1.软件

Postgresql v9.5

Postgis v2.5

pgRouting v2.2

1.2.数据

point:点数据,模拟poi

road:线数据,模拟路网

数据下载见文末

2.数据处理

以下数据均为模拟数据。

要对路网数据构建拓扑,首先需要将路网数据中的路段按照连接点打断。在图 1中可以看到,在两条线路交叉的位置是不存在有节点的,这对于两条路相交的路来说是不正确的,是高架桥、立交桥这种模型。简单起见,假设路网中的所有线路都是相交的。

为此,首先使用ArcGIS中高级编辑工具–>打断相交线工具将所有的路网数据线在相交处打断。只需要在编辑状态下选中全部线数据,点击“打断相交线工具”即可。(如图 2所示)

打断相交线后,其效果如图 3所示,通过查看属性表(如图 4),可以看到原本的10条线段已经变为了26条。

image.png

图 1

image.pngimage.png

图 2

image.png

图 3

image.pngimage.png

图 4

3.QGIS导入数据

数据处理完毕后,使用QGIS将数据导入到Postgresql中。此之前,已经在Postgresql中创建了名为PgRoutingTest的数据库,并使用如下SQL添加了PostGIS和pgRouting扩展。

1
`CREATE EXTENSION postgis; CREATE EXTENSION postgis_topology; CREATE EXTENSION pgrouting;`

打开QGIS,在右侧PostGIS右键选择新建连接(图 5),然后在新建连接面板上填写连接的相关信息(图 6),其中1处为数据库的名称,2处为数据库的用户名、密码,点击3可以测试数据库能否成功连接。

image.png

图 5

image.png

图 6

成功添加连接后,将数据加载进QGIS,打开上方数据库|数据库管理器菜单,进入到PostGIS|PgRouting中(图 7),然后点击上方的“导入图层或文件”按钮,打开导入矢量图层面板(图 8)。在输入中选择要导入的图层,在输出表格中,架构可以为空,默认为public,表格输入当前图层导入数据库后的表格名,在主键位置处选择id(事先经过处理,保存其唯一性),其他的按照需要自行配置即可。最后点击OK即可将图层导入到数据库中。

同上,将road数据也导入到数据库中。

image.png

图 7

image.png

图 8

4.构建拓扑路网

4.1.修改路网数据表

为路网数据表添加三个字段:source、target、length,source、target代表路线的起始点,length代表路线长度。

1
`ALTER TABLE road ADD COLUMN source INTEGER; ALTER TABLE road ADD COLUMN target INTEGER; ALTER TABLE road ADD COLUMN length FLOAT8;`

4.2.创建路网拓扑信息

语法如下:

1
`SELECT pgr_createTopology(text edge_table, double precision tolerance, text the_geom:='the_geom', text id:='id', text source:='source',text target:='target', text rows_where:='true', boolean clean:=false);`

从the_geom开始,后面的参数都是可选的,各个参数的意义如下:

edge_table:路网数据表名;

tolerance:创建拓扑的距离容差;

the_geom:几何信息的字段名;

id:路网数据表的主键名;

source和target就是在3.1中创建的source和target字段;

rows_where:路网中建立拓扑的行进行筛选,相当于where字句的内容;

clean:是否清除之前建立的拓扑。

例如:

1
`SELECT pgr_createTopology('road',0.000001,'geom','id' , rows_where:='id in (xxx)');`

执行之后,数据库中会出现一个xxx_vertices_pgr的表格,xxx为edge_table的值,这里直接使用如下语句就行,如果返回OK,则表明拓扑创建成功,并且数据库中会多出名为road_vertices_pgr的表格。

1
`SELECT pgr_createTopology('road',0.000001,'geom','id');`

4.3.更新路网数据表中length

length代表路网中各条边的成本例如距离、时间等,此处使用距离代表各边的成本。语法如下:

1
`UPDATE road SET length = ST_Length(geom::geography);`

在实际使用时,由于可能会更新路网数据,因此可以在UPDATE语句中加入where条件。

4.4.路网节点与点图层关联

下一步计算point表中离路网最近节点,并将节点编号保存在数据库中。

首先创建字段nearest_node_id,用于保存路网中最近距离节点主键。

1
`Alter Table point Add Column nearest_node_id INTEGER;`

由于point一般是不变的,因此我们不必每次查询是动态查找point最近的路网节点,可以将最近路网节点的id写入到数据库中,这段SQL比较复杂,详细了解请到stackoverflow

1
`UPDATE point p1 SET nearest_node_id=sub.vid FROM ( SELECT p.pid, b.id as vid FROM ( SELECT point.id as pid, point.* FROM point ) p, LATERAL ( SELECT road_vertices_pgr.id FROM road_vertices_pgr ORDER BY p.geom <-> road_vertices_pgr.the_geom LIMIT 1 ) b ) sub WHERE p1.id = sub.pid;`

5.最短路径查询

pgRouting的最短路径算法有多种,A*算法、Dijkstra算法、Floyd-Warshall算法等等,详见官网。这里使用最常见的Dijkstra算法。

我们假设要从A点(105, 31)到B点的最短路径,其最短路径目测如图 9所示。

image.png

图 9 (大致示意,省略中间部分节点路段)

5.1.计算距离起点最近的路网节点

使用如下语句,查询距离改点最近的路网节点,执行的效果如图 10所示,找到的最近的节点Id为1。

1
`SELECT id, ST_AsText(the_geom) as geom, ST_Distance_Sphere(ST_GeomFromText('POINT(105 31)',4326), v.the_geom) as distance FROM road_vertices_pgr v WHERE ST_DWithin(ST_GeomFromText('POINT(105 31)',4326), v.the_geom, 5) ORDER BY ST_GeomFromText('POINT(105 31)',4326)<-> v.the_geom LIMIT 1;`

image.png

图 10

5.2.查找终点最近路网节点Id

由于point图层中的最近路网节点Id应存在数据库中,所以直接查询数据库point表即可得到(图 11),距离p2最近的路网节点id为13,这与我们预测不符合。

1
`SELECT nearest_node_id FROM point WHERE name='p2';`

image.png

图 11

在QGIS中使用测量工具,发现13号路网节点距离p2的距离确实更近(图 12)。

image.png

图 12 (a)

image.png

图 12 (b)

图 12

5.3.最短路径查询

获取起始点在路网节点中的id后,使用pgr_dijkstra函数计算两节点间的最短路径,SQL如下,其中查询结果(图 13)中的seq表路径次序,node表示经过的节点,edge表示经过的边,cost表示路径成本(这里就是路长),edge_geom表示该路段的几何信息。将查询结果导入为新图层导入到QGIS中,并设置符号化之后结果如图 14所示。

1
`SELECT d.seq, d.node, d.edge, d.cost, ST_AsText(e.geom) AS edge_geom FROM pgr_dijkstra( 'SELECT id, source, target, length AS cost FROM road', -- 用于计算最短路径的路网 1, -- 起点节点id 13, -- 终点节点id FALSE -- 无向路径 ) as d LEFT JOIN road AS e ON d.edge = e.id ORDER BY d.seq;`

image.png

图 13

image.png

图 14

5.4.完善

在4.3中已经得到了路网中的最短距离路线,接下来还要将起点、终点到路网中的线路加上,这里有两种方案:

  1. 计算起点到路网中第一路段的最近点与终点到最后一路段的最近点,然后截取查询后路网,并将起点、终点连接进去。其结果示意图如图 15所示。

image.png

图 15 (大致示意,省略中间部分节点路段)

  1. 直接将起点终点与路网中的最近节点相连接。其结果示意图如图 16所示。

image.png

图 16 (大致示意,省略中间部分节点路段)

明显,第二种方案更为简单。在4.1中,已经查询出来了路网中1号节点(与起点最近节点)的几何坐标以及起点与1号节点的距离,在4.2中查询到了距离终点最近的节点id(13),通过这个节点id (13)可以在road_vertices_pgr表中查询到这个节点的几何信息,通过计算节点13与终点的距离,即可得到整段线路的长度。此处比较简单,不在赘述。

附:本文所使用的shapefile数据 下载

由 Hexo 驱动 & 主题 Keep
总字数 105.7k 访客数 访问量