☰
PostGIS+pgRouting实战:路网拓扑、最短路径与成本计算全解析
2026/10/15 5:08:54 网站建设 项目流程

1. 项目概述与整体设计思路

先说说这个项目是干什么的。如果你做过GIS数据开发,或者接触过订单配送、车辆调度这类系统,大概率都遇到过“给定一个路网,怎么算出从A点到B点的最优路线”这个需求。pgRouting是PostGIS下的一个开源扩展,专门干这件事:它把路网数据抽象成图,然后用各种图论算法(最短路径、旅行商、可达范围等)算出你想要的路径结果。我这次要分享的就是一套完整的“PostGIS+pgRouting”实战过程,从建表、灌数据、建拓扑,到跑出最短路径、按时间和距离算代价,再到最后的常见坑位排查,一条线走通。

这个项目最典型的场景是:你已经有一份带几何线段的道路数据(比如从公开地图导出的shp,或者自己矢量化好的规划路网),但里面没有起点/终点编号,没有通行方向,更没有算好的通行时间。你需要把这些线段“拼”成一个可计算的网络——这正是pgRouting最擅长的事情。当然,它也能用在等时圈分析、多点配送顺序优化、或者能力评估类的数据可视化项目里。

什么人适合参考这篇文章?如果你是刚接触GIS开发的程序员,或者已经是使用PostGIS多年但还没碰过pgRouting的数据库工程师,这篇文章的设置就是“拿起来就能用”的类型。读完你会知道:路网表该设计哪些字段,拓扑为什么必须建,Dijkstra和A*到底怎么选,以及真遇到“明明两个点很近却算不出路径”时该怎么处理。我不会把每个函数的手册照搬一遍,而是把真正能落到工程里的步骤和教训讲清楚。

2. 环境准备与路网数据建模

2.1 版本选型与扩展安装

pgRouting依赖于PostGIS和PostgreSQL,所以先得确认这三者能配套运行。我实测比较稳定的组合是:PostgreSQL 13以上、PostGIS 3.x、pgRouting 3.x,三者的安装基本可以用系统包管理器直接解决。比如在Ubuntu类系统下:

sudo apt install postgresql postgresql-contrib postgis postgresql-12-postgis-3 \ postgresql-12-pgrouting

注意包名里的数据库版本号要和你的PostgreSQL小版本一致。装完之后进入你的库执行:

CREATE EXTENSION postgis; CREATE EXTENSION pgrouting;

执行完你可以在pgAdmin左侧扩展目录里看到两个扩展已被启用。如果用的是Docker,推荐直接拉官方镜像,省得编译半天,比如postgis/postgis镜像通常也带了pgRouting。Windows用户去官网下载安装包,不赘述,但记住一个原则:别混着装不同版本的PostGIS和pgRouting,容易发生符号链接缺失,启动函数时报一堆找不到依赖。

提示:如果你在公司内网环境,装完扩展后在测试阶段尽量先用官方样例数据跑通流程,再换自己的业务数据。这样能快速区分是扩展问题还是数据问题。

2.2 路网表设计

路网表是路径分析的“原材料”。下面这个结构是pgRouting官方推荐的,加上我改造过的一些实用字段,适合大多数车辆配送场景。

CREATE TABLE ways ( id BIGINT PRIMARY KEY, osm_id BIGINT, -- 原始路网ID,调试用 name TEXT, -- 道路名 geom GEOMETRY(LINESTRING, 4326), source BIGINT, -- 起点拓扑编号 target BIGINT, -- 终点拓扑编号 cost DOUBLE PRECISION, -- 正向通行代价 reverse_cost DOUBLE PRECISION, -- 反向通行代价 oneway INT DEFAULT 0, -- 0双向,1正向单行,2反向单行 speed_kph DOUBLE PRECISION, -- 限速 mode TEXT DEFAULT 'car', -- 通行模式 toll DOUBLE PRECISION DEFAULT 0, -- 收费额,可选 maxheight DOUBLE PRECISION, -- 限高,用于躲避受限路段 maxweight DOUBLE PRECISION -- 限重,同上 );

我专门加了oneway字段,在后面构建拓扑时配合cost和reverse_cost才能正确处理单行道。很多新手只建了cost,结果单行道不仅能反着走,还会在路径选择里被反向使用,最终结果错了自己都不知道。

2.3 路线数据来源和导入思路

最省事的方式是下载OpenStreetMap的某区域路网,用osm2pgrouting工具一键导入,它会直接生成ways、nodes、relations等带拓扑的表。这个思路适合快速做原型。不过在我实际项目里,很多路网是自己的CAD或shp,OSM不一定有。那就要走一个自己写导入器的路线。

如果是shp文件,最简单的做法是用PostGIS的shp2pgsql命令:

shp2pgsql -I -s 4326 road.shp public.ways_raw | psql -U username yourdb

导入后把ways表和ways_raw对应上,把geom的坐标系统一成GCJ-02或WGS84也行,关键是geom必须是LINESTRING。如果要素是MultiLineString,先拆成单线再入库,因为pgRouting的拓扑函数在LINE上处理最稳。

关于坐标系有个很要命的问题:如果路线在适合米制计算的本地投影下,比如EPSG:3857或EPSG:32650,那距离计算直接用ST_Length就会返回米。如果统一存在4326下计算距离,得到的是度,跑出的路径会完全偏掉。我的习惯是:所有几何统一存4326,计算时用ST_Transform到本地投影算长度和时间,最后输出再转回4326给前端展示。这样能保证几何和成本都准确。

2.4 连接线段与打断处理

很多原始路网数据是一条长线横跨好几个路口,如果不把长线在路口处“打断”成多个线段,路径就无法在这个点转向。这是个容易忽略的元问题。pgRouting里的pgr_nodeNetwork可以自动打断相交线,但实际使用效果依赖容差和精度。

我建议导入前在QGIS里先用GRASS或SAGA的“线转交点”工具预处理,或者使用PostGIS的ST_Node:

UPDATE ways SET geom = ( SELECT ST_Node(ST_Collect(geom)) FROM ways );

注意这会把所有线打散成一个MultiLineString,你得后续拆回单条LineString并重新分配ID。工程上更可控的做法是利用pgr_nodeNetwork,但必须设置好tolerance参数,否则会判断错误。对了,pgr_nodeNetwork执行完会在原表基础上生成新表,例如ways_noded,你后面要用新表做拓扑,别被原表里的旧source/target带偏。

实操心得:宁可打断多也尽量不要打断少。如果一条路线上本应存在的路口没有打断,后建的source/target数量就会少一截,两个路段之间没法转向,路径就会绕远甚至无法连通。

3. 拓扑构建与成本计算

3.1 为什么必须先建拓扑

“拓扑”这个词听起来玄乎,说白了就是给每条线的起点和终点分别指定一个数字ID,让计算机知道哪些线段在端点处是连接在一起的。你可以把每条道路的端点想成地铁站的出入口,线段就是两站之间的轨道。如果两条线段共享同一个站台(即同一坐标点被识别为同一个节点),那么列车就能在站台换乘。pgRouting的路径算法并不会自己判断几何上的“相交且相连”,它只看source和target两个字段。因此构建拓扑是整个流程中最关键的一步。

在pgRouting里用pgr_createTopology一键生成source/target:

SELECT pgr_createTopology( 'ways', -- 表名 0.00001, -- 容差(单位与坐标系一致) 'geom', 'id' );

执行完后会在同schema下生成ways_vertices_pgr表,里面每个节点有一行记录,带有拓扑编号和几何点。只要source和target落在这个表里,就说明线段已经正确接上节点了。

容差值得重点说:如果几何坐标系是度,那0.00001大约是1米左右;如果几何是米制投影,就用0.5或1米。你给的容差太小,则本应在路口相接的端点可能因几厘米的偏移被识别成两个节点;容差太大,则会把不该连接的道路在交叉口直接粘连。所以建议先用SQL看看数据里端点间的实际空隙大小,再决定容差。

3.2 检查拓扑质量

建完拓扑先别急着跑路径,先做体检。常见体检SQL包括:

-- 看多少行没被赋source/target SELECT count(*) FROM ways WHERE source IS NULL OR target IS NULL; -- 看是否存在source==target的死亡段 SELECT count(*) FROM ways WHERE source = target; -- 看节点数量 SELECT count(*) FROM ways_vertices_pgr;

我遇到过最典型的问题是:一条公路被分成两个重叠但偏移了不到1米的线段,比如上下行线分开采集,结果拓扑后中间路段莫名其妙断链。这时候可以把线段端点和vertices_pgr的节点位置做空间对比,检查是否有多余的节点断裂。调整容差后重跑pgr_createTopology并不会让你后悔,放心重跑。

如果从shp导入的数据在交叉口存在形状点(中间点)落在了另外一条线的顶点上,导致两线实际上有接触但source没共享,这时同样可以通过容差解决。不过原始数据是那种“高架在上、地面道路在下”的立体交叉,你会在垂直投影里看到几何相交,但实际物理上不该互通。此时光靠容差处理不了,必须在数据源中为高架和地面道路设置分层标识,比如level字段,在计算时按level过滤掉不同层道路的拓扑连接。pgRouting本身不解决三维立交问题,这是行业里公认的坑。

3.3 成本怎么设置:距离与时间

路径算法里的cost是“代价”而不是“长度”。你可以按距离、时间、点费,甚至道路舒适程度来定义。最常用的两种:

  • 按距离:cost = ST_Length(geom),单位根据EPSG决定。
  • 按时间:cost = ST_Length(geom) / (speed_kph * 1000 / 3600),得到小时。如果希望以分钟为单位,则乘以60。

举个例子,一条线length=1000m,限速30km/h,那通过它需要约2分钟。SQL写成:

UPDATE ways SET cost = ST_Length(ST_Transform(geom, 3857)) / (speed_kph * 1000.0 / 3600.0) * 60, reverse_cost = ST_Length(ST_Transform(geom, 3857)) / (avg_speed_kph * 1000.0 / 3600.0) * 60;

其中avg_speed_kph可以按道路等级设定。如果是双线道路,reverse_cost与cost相同;如果是单行道,那么reverse_cost设置成-1,表示不可通行。

在pgRouting里,所有算法的负值边默认是“禁止通行”,而不是“反向”。所以在SQL里我们既保留了真实正向反向两个代价字段,又把不可通行方向置为-1。这样可以做到:双行线正反都可走,单行线只有正确方向能走,禁行线直接-1封死。

3.4 转向成本与复杂路口

基础的最短路径不考虑“左转要等红绿灯”“右转更快”这类转向成本。真实导航系统里的转向成本是限制条件:大路口不允许调头、某些路口禁止左转等。pgRouting的系列函数里有pgr_trsp(Turn Restriction Shortest Path)专门处理转向限制。它需要识别路口的进入边和离开边,并对每条边对设置禁止转向关系。

如果你的业务还没到那么细的级别,我建议先跳过转向成本,把基础路径跑通,再考虑增加转向限制。因为转向限制表的构建和维护工作量大,且容易出错。很多物流配送项目其实只用距离和时间做排序,并不会严格还原地图导航里的左转灯规则。

项目里怎么取舍?先把基础算法落地,给业务方看效果,再谈转向限制。如果一开始就追求100%精确,项目进度会被拖垮。

4. 路径分析核心算法与SQL实战

4.1 核心函数族一图看清

pgRouting里的路径算法很多,我用一张表列一下比较常用的:

函数用途关键参数典型场景
pgr_dijkstra单点到单点最短路,支持双向cost起点ID、终点ID日常点对点路径
pgr_dijkstraCost只求最短路径的总代价起点ID、终点ID批量算距离或时间成本
pgr_astarA*启发式最短路,更快起终点坐标/几何大路网性能敏感场景
pgr_kspK条最短路径起点、终点、K值备选路线规划
pgr_drivingDistance从某点出发指定成本范围内的可达点起点、成本上限等时圈、服务半径分析
pgr_withPoints起点或终点沿着道路某位置接入point-table门到门路径分析
pgr_TSP旅行商问题,多点顺序优化点集配送顺序优化

其中pgr_dijkstra是最核心、最容易上手的入口。A本质上在道路网格上用了启发式距离估计,性能更优,但需要提供起终点的坐标,且对代价函数是否满足三角不等式比较敏感,我建议先用Dijkstra验证逻辑,再切A做性能优化。

4.2 点对点最短路径:从抽象节点到几何路径

假设你已经在ways表上建好了拓扑,现在想求某个节点(比如4012)到另一个节点(6008)的最短路径:

SELECT seq, node, edge, cost, geom FROM pgr_dijkstra( 'SELECT id, source, target, cost, reverse_cost FROM ways', 4012, 6008, directed := true ) AS di JOIN ways w ON w.id = di.edge;

重点说一下directed := true:它表示网络是有向图,会使用cost作为正向边代价、reverse_cost作为反向边代价,并且如果reverse_cost是负值就禁止反向通行。这是真实道路网的标准配置。如果你把所有道路都视为无向,单行道和禁行规则就被忽略了。

返回的geom是分段线,前端或数据服务需要把多段结果合并成一条路线。合并的SQL可以这样写:

SELECT ST_LineMerge(ST_Collect(di.geom)) AS route_geom, SUM(di.cost) AS total_cost FROM ( SELECT w.geom, di.cost, di.edge FROM pgr_dijkstra( 'SELECT id, source, target, cost, reverse_cost FROM ways', 4012, 6008, directed := true ) AS di JOIN ways w ON w.id = di.edge ) AS di;

这里ST_LineMerge(ST_Collect(...))会把所有线段按端点合并成一条完整的LineString。如果你遇到ST_Collect后出现MultiLineString,说明拓扑出现了断链——某个相邻边没有共享端点,要回头检查拓扑。

在实际业务中,用户通常给的是经纬度而不是拓扑编号,所以你需要先做“最近节点匹配”:通过空间查询找到离任意经纬度最近的节点ID,然后把它传给算法。SQL大概是:

SELECT id FROM ways_vertices_pgr ORDER BY geom <-> ST_SetSRID(ST_MakePoint(116.40, 39.90), 4326) LIMIT 1;

这就是点匹配。但如果匹配到的是某个路线的中间点(不是端点),很多项目还需要做“线内点投影”。pgRouting提供了pgr_withPoints处理这类问题。它允许把用户坐标匹配到边上,再以边内偏移量作为成本,形成无缝的起点终点路径。这个更贴近真实地图App的体验。简单门对门分析时,直接匹配到顶点通常够用,但如果路网很稀疏,匹配顶点会造成路径明显绕路,这时建议直接上pgr_withPoints。

4.3 批量计算代价:在业务里做排序

如果你有一个配送订单表,里面有几十个终点,最常用的做法不是逐条调用点对点算法,而是用pgr_dijkstraCost做批量成本计算。它不返回路径几何,只返回总代价,这样性能极高。SQL可以写成:

SELECT start_vid, end_vid, agg_cost FROM pgr_dijkstraCost( 'SELECT id, source, target, cost, reverse_cost FROM ways', ARRAY[4012, 4015, 4032], ARRAY[6008, 6010], directed := true );

这种方式会形成一个多对多的矩阵,比如3个起点、2个终点,会得到6行结果。在车辆调度系统里,这个结果可以直接交给下单排序模块,按时间最短或成本最低来推荐配送顺序。

特别注意:agg_cost只统计成功的线路。如果起点和终点不在同一连通子图内,这一行不会返回结果或返回agg_cost为无穷大。所以批量跑之前,最好先用连通性检查把孤立子集找出来,不然订单会莫名其妙消失。

4.4 等时圈与可达范围分析

很多时候你不想只算一条路径,而想看“从仓库出发15分钟内能到达哪些区域”。这时候pgr_drivingDistance最合适。它从起点出发,在指定的成本上限内扩展,返回所有可达节点。SQL示例:

SELECT * FROM pgr_drivingDistance( 'SELECT id, source::INTEGER, target::INTEGER, cost, reverse_cost FROM ways', 4012, 30, -- 30分钟 directed := true );

这里成本单位是分钟。返回结果里的node是可达节点ID,agg_cost是到达该节点累计的时间。要形成“等时圈”的可视化面,可以把每个可达节点的道路几何聚合起来,或者用这些节点的外包络面做缓冲区,再叠加到地图上。更精细的做法是利用pgr_drivingDistance返回的边ID,把整个子图导出,然后渲染成道路可达范围。

等时圈在小范围配送半径分析、门店覆盖评估里特别常用。我实际做过的店铺选址评估里,就用15分钟车行圈的范围去跟商圈人口网格做叠加,从而算出覆盖人数。只要你有路网和POI网格,这套流程能比单纯画圆形缓冲区精准得多。

4.5 备选路线与旅行商扩展

如果你需要给客户展示“最短”、“次短”、“第三短”几条备选路线,pgRouting里对应pgr_ksp。它的核心思想是每次算出最短路后,把路径上的某些边禁用或用额外代价惩罚,再继续搜索,直到找到K条候选路径。使用起来很简单:

SELECT seq, route_id, path_seq, node, edge, cost FROM pgr_ksp( 'SELECT id, source, target, cost, reverse_cost FROM ways', 4012, 6008, 3, -- 3条备选路径 directed := true, heap_paths := false );

注意ksp的结果里route_id区分不同候选路径,需要把相同route_id的行聚合成一条完整路线。我经验里heap_paths := true在一些版本上有性能问题,如果只是展示备选,拿到3条就够,不必追求算法最大完整性。

旅行商问题在多配送点顺序优化里也很有用。pgr_TSP是基于矩阵成本计算的一个实现,但“恶魔在于时间窗口和车辆容量”这些物流约束它管不了。我通常只在做静态顺序建议时用,真正运营级的排线还是要上约束求解器。作为入门可以跑通官方红尘样例,先理解“从矩阵到顺序”这件事。

5. 常见问题与排查技巧实录

5.1 起终点不在同一连通子图

这是报错之外最让人头痛的现象:SQL正常返回,但结果为空,页面不画线。绝大多数是因为路网被打断后,起点或者终点所在的道路属于孤岛,和整个网络没连通。排查方法,先跑:

SELECT source, target FROM ways;

找出你需要的那条边上对应的节点,然后看这两个节点是否属于同一个连通分量。pgRouting有pgr_connectedComponents,可以对整个图做连通性分析:

SELECT component, count(*) FROM pgr_connectedComponents( 'SELECT id, source, target, cost, reverse_cost FROM ways' ) GROUP BY component ORDER BY count(*) DESC;

如果你起点在component=5,终点在component=2,那这俩根本不在同一个交通网络里。原因多半是数据的区域性缺口,比如两片道路中间没被矢量化,或者打断后两片区域之间的连接线被过滤掉了。

解决方式:一是回数据源补齐缺失道路;二是如果允许,在几何上最近的端点之间补一条“虚拟连接线”(注意要设置正确的cost和道路等级,否则算出来的路径会穿过不可行驶区域,业务上不现实);三是给业务方明确提示“该区域无法通行”。不要为了出图而强行把不连通的网络连起来,会严重破坏后续成本分析。

5.2 source/target建好后仍然看不到路径

有次我新导入路网后,连官方样例模板都能跑,但自己的表却一直报错。后来发现一个很隐蔽的问题:ways表的source和target字段类型是BIGINT,而pgr_dijkstra的SQL字符串需要返回整数类型,通常它要求id, source, target都是INTEGER。如果你的id是BIGINT,在最外层传字符串时最好用source::INTEGER、target::INTEGER。这不会影响数据量,但能避免很多版本兼容问题。遇到报错说“function pgr_dijkstra(unknown, ...)”就可以检查这里。

还有一次是执行pgr_createTopology之后,我顺手把ways_vertices_pgr表里的id当成了source,却没意识到vertices的id是自增的,和ways.source的对应关系是由拓扑生成的。如果你擅自更新过 source/target,比如手动修过某条线,可能导致顶点不一致。所以尽量别再手动“修路”,要用pgr_createTopology重建。

5.3 负值reverse_cost的误用

最常见的逻辑错误是:我把一条路两方向赋了相反值,比如正向cost=10,反向reverse_cost=-1,结果directed := true时这条路确实不能反向走,但我其实想表达“这条路反向很堵,所以代价高”,那应该给reverse_cost=200而不是 -1。记住:-1在pgRouting里是“禁止通行”,不是“代价无穷但概念上可用”。如果你希望表示高代价,直接用超大正数比如999999,但别用 -1。负值只应该在“这一方向不能走”时出现。

如果你跑出来的路线奇怪地绕了一个大圈,首先检查有没有把单行道的反向写成正值,那等于允许它反向行驶了,相对正确算法可能更短路。这时把目标路段的reverse_cost改成 -1 再看效果。

5.4 空间索引与性能优化

路网节点几万到几十万,如果不加索引,路径算法里的顶点邻接查询会非常慢。拓扑建完后,一定要给ways_vertices_pgr建id主键、给ways表的source和target建普通索引,以及给geom建GIST空间索引。性能差异是数量级的。

CREATE INDEX idx_ways_source ON ways(source); CREATE INDEX idx_ways_target ON ways(target); CREATE INDEX idx_ways_geom ON ways USING gist(geom); CREATE INDEX idx_vertices_id ON ways_vertices_pgr(id); CREATE INDEX idx_vertices_geom ON ways_vertices_pgr USING gist(geom);

还要注意算法函数接收的是“SQL字符串”,它会动态执行。每次点对点请求都会执行一次SQL并计算背景网络,如果你在Web接口里频繁调用,建议用pgr_dijkstra结合预计算的连通分量信息做“先过滤、再计算”。更进阶的优化是使用pgr_dijkstraCost做离线矩阵预计算,将矩阵结果存到Redis或业务表里,在线查询只做矩阵读取。

5.5 路线平滑与展示层面处理

算法输出的路径是沿路网线段的原样几何,看起来像锯齿状折线,尤其当原始路网数据在道路交叉口没有圆角弧线时,展示在地图上会比较“生硬”。我常用的一个做法是ST_Simplify再加ST_LineInterpolatePoint做抽稀,但要小心简化过头会把道路本身的小转弯磨平,从而引起用户对“为什么不经过辅路”的质疑。

另一个经验是渲染前最好把路径“吸附”到正确车道。如果原始路网是双线,最短路径可能选择靠左或靠右的线段,视觉上会偏离GPS实际轨迹。一个折中办法是输出一条居中的中心线路径,前端地图不显示高精度车道,只显示道路主干走向。移动端导航更精细的需求,还得引入专门的导航SDK,不在pgRouting的职责范围内。

6. 实务扩展:结合业务落地时的几个建议

6.1 从“能算”到“算得准”

很多项目在跑通Dijkstra后,就匆忙上线,结果业务方拿GPS实际轨迹一对比,反馈偏差大。问题基本集中在两点:一是speed_kph是拍脑袋填的,二是没有考虑红绿灯和拥堵。pgRouting本身不感知实时路况,它的定位是“基于静态路网的成本计算”。如果你需要按早晚高峰调整通行时间,可以采用外部路况API返回各路段速度因子,然后在查询前更新cost字段。比如每小时全量刷新一次cost = realtime_speed_kph对应的通行时间。这样做能让路径推荐更贴近实际,但不能太依赖实时性,万一API罢工,要自动回退到默认速度。

6.2 如何把路径结果提供给业务系统

我做过的最常见的接口设计是:HTTP请求传入起点/终点经纬度,后端先做点匹配,再调pgRouting获得路径几何和代价,最后返回GeoJSON给前端。GeoJSON的生成可以直接用PostGIS JSON函数:

SELECT json_build_object( 'type', 'Feature', 'geometry', ST_AsGeoJSON(ST_LineMerge(ST_Collect(geom)))::json, 'properties', json_build_object('total_cost', total_cost) ) AS geojson FROM ...

如果数据量较大,建议不要把完整路径Geojson直接塞进数据库返回字符串,可以考虑使用PostGIS的ST_AsMVT矢量瓦片方式,在前端动态加载。路径分析结果大部分情况下只需要一条折线,GeoJSON简单直接,不用担心性能瓶颈。

6.3 结合业务字段做多模式路径

你的路网表里如果有mode字段,最理想的算法输入是“动态过滤 + 成本修正”:

SELECT pgr_dijkstra( 'SELECT id, source, target, CASE WHEN mode IN (''car'',''truck'')) THEN cost ELSE 999999 END AS cost, reverse_cost FROM ways WHERE mode IN (''car'',''truck'')', start_vid, end_vid, directed := true );

用999999代替-1其实是因为某些路对指定交通方式计入很大的惩罚,而不是完全禁止,这样路径在极端缺少道路时仍能强行通过,但会优先绕开。限高、限重字段也类似,把不能通行的路段成本调成超大值即可。

我做园区物流项目时,货车路线规划必须过滤掉限高3米以下的路段,一开始直接过滤掉这些边,导致很多小园区内的路网被切成碎片,货车根本算不出来路线。后面改成“优先避开限制路段”:先过滤掉不可通行边,如果没路可走,再回退到可通行但限高路段,并大幅度惩罚成本。这个“软硬结合”的思路值得你在处理复杂规则时借鉴。

7. 一个完整的最小实战:从建表到出路径

最后,我把整个过程浓缩成一条可以直接抄的“最小可复现”链路。假设路网表已经导入,字段如上。

第一步,确认扩展:

CREATE EXTENSION IF NOT EXISTS postgis; CREATE EXTENSION IF NOT EXISTS pgrouting;

第二步,给几何字段统一坐标和连接线:

ALTER TABLE ways ALTER COLUMN geom TYPE GEOMETRY(LINESTRING, 4326) USING ST_Transform(geom, 4326);

第三步,建拓扑:

ALTER TABLE ways ADD COLUMN source INT; ALTER TABLE ways ADD COLUMN target INT; SELECT pgr_createTopology('ways', 0.00001, 'geom', 'id');

第四步,计算cost和reverse_cost:

ALTER TABLE ways ADD COLUMN cost DOUBLE PRECISION; ALTER TABLE ways ADD COLUMN reverse_cost DOUBLE PRECISION; UPDATE ways SET cost = CASE WHEN oneway IN (0,1) THEN ST_Length(ST_Transform(geom, 3857)) / (speed_kph * 1000.0 / 3600.0) * 60 ELSE -1 END, reverse_cost = CASE WHEN oneway IN (0,2) THEN ST_Length(ST_Transform(geom, 3857)) / (speed_kph * 1000.0 / 3600.0) * 60 ELSE -1 END;

第五步,跑路径:

SELECT * FROM pgr_dijkstra( 'SELECT id, source, target, cost, reverse_cost FROM ways', 1, 100, directed := true );

如果你在业务库上跑通了这五步,那说明最基本的链路已经没问题了。再回去看前面提到的连通性检查、成本设计和算法选型,把它逐步武装成生产级方案。

pgRouting是一个只要你认真对待数据就能得到靠谱结果的工具,但它绝对不是“给一个点,自动吐一条路”的黑魔法。我个人做下来最大的体会是:整个项目的关键不在算法调用,而在数据预处理和拓扑正确性。你花在道路打断、成本定义、连通性校验上的时间,往往比查函数文档的时间多得多。前期把这些基础做扎实,后面不管是做点对点、等时圈还是配送排序,都会顺手很多。最后再分享一点:每次修改路网后,别急着改cost,先跑一遍连通性分析,再看路径结果是否合理。习惯养成之后,那些“莫名其妙没路线”的突发问题至少会少一半。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询