PgRouting求解大数据量最短路径
Pgrouting求解大數據量最短路徑
實際工作中的一個場景,類似于要做一個像地圖那樣,指定起終點,給出所有可行路線,本來是自己實現的,使用圖的深度優先算法,結果由于數據量太大了,直接把內存算崩了。我也知道可以大而化小、分而治之、小則建立關系,可惜這樣一個好的數據結構,我搞不出來。最終不得已,選用pgrouting作為替代品,但也跟原需求不太符合,這個是個求最優的方式。
唉,不過是一個離了開源啥也干不了的廢物罷了。
也許,這才是許許多多CRUD碼農,亟待突破的瓶頸!
所以,努力學習吧!
首先要有一個postgresql數據庫,并開啟postgis與pgrouting擴展,這里不多贅述!查看具體的安裝過程。
導出 | OpenStreetMap
pgRouting通過osm2pgrouting加載osm地圖數據
github|管網連通性分析
pgr_dijkstra — pgRouting Manual (3.3)
pgRouting教程四:準備數據 - 知乎
pgrouting - 如何修復 pgr_dijkstra 錯誤,查詢必須返回列 ‘id’、‘source’、‘target’ 和 ‘cost’? - 地理信息系統堆棧交換
postgresql對單引號進行轉義
postgresql - 如何編寫一個不返回任何內容的 postgres 存儲過程? - 堆棧溢出
common table expression - PostgreSQL: Query has no destination for result data - Stack Overflow
創建觸發器-PostgreSQL輕松學-SJK66.COM
返回NULL的行級觸發器導致錯誤:查詢沒有結果數據的目的地 - Thinbug
一、導入OSM數據
開源地圖導出 | OpenStreetMap,通過地圖導出自定義的位置信息。
進入Linux,下載地圖信息,并導入信息。
# 忽略證書下載后重命名為map.osm wget https://www.openstreetmap.org/api/0.6/map?bbox=116.3849,39.9093,116.3969,39.9226 -O map.osm --no-check-certificate # 導入數據 osm2pgrouting -f 地圖數據 -h 數據庫host -U 數據庫用戶名 -d 數據庫名稱 -p 數據庫端口 -W 數據庫密碼 --conf=/usr/share/osm2pgrouting/mapconfig_for_cars.xml rm 地圖數據導入后的輸出信息如下
[root@rollback ~]# osm2pgrouting -f map.osm -h 192.168.10.10 -U pgrouting -d pgrouting_test -p 5432 -W meethigher --conf=/usr/share/osm2pgrouting/mapconfig_for_cars.xml rm map.osm Execution starts at: Wed Apr 27 16:11:47 2022***************************************************COMMAND LINE CONFIGURATION * *************************************************** Filename = map.osm Configuration file = /usr/share/osm2pgrouting/mapconfig_for_cars.xml host = 192.168.10.10 port = 5432 dbname = pgrouting_test username = pgrouting schema= prefix = suffix = Don't drop tables Don't create indexes Don't add OSM nodes *************************************************** Testing database connection: pgrouting_test database connection successful: pgrouting_test Connecting to the database connection successCreating tables... TABLE: ways_vertices_pgr created ... OK. TABLE: ways created ... OK. TABLE: pointsofinterest created ... OK. TABLE: configuration created ... OK. Opening configuration file: /usr/share/osm2pgrouting/mapconfig_for_cars.xmlParsing configurationExporting configuration ...- Done Counting lines ...- Done Opening data file: map.osm total lines: 78998Parsing dataEnd Of fileFinish Parsing dataAdding auxiliary tables to database...Export Ways ...Processing 4493 ways: [**************************************************|] (100%) Total processed: 4493 Vertices inserted: 174 Split ways inserted 172Creating indexes ...Processing Points of Interest ... ######################### size of streets: 4493 Execution started at: Wed Apr 27 16:11:47 2022 Execution ended at: Wed Apr 27 16:11:47 2022 Elapsed time: 0.459 Seconds. User CPU time: -> 0.16 seconds #########################推薦使用dbeaver打開,可以直接查看地圖數據,也是基于OpenStreetMap的。
因為OpenStreetMap下載的數據,本身已經經過拓撲處理了,所以就可以直接進行查詢。
-- 函數使用說明 pgr_dijkstra(Edges SQL, 起點編號, 終點編號 [, directed])-- 使用dijkstra查詢最短路徑 SELECT * FROM pgr_dijkstra('SELECT gid as id, source, target, cost FROM ways',30,56 );起點編號source和終點編號source是pgrouting維護的。通過dbeaver可以直觀的看出起終點號。
二、Dijkstra最短路徑
使用自己創建的數據庫,來體驗下整個流程。
-- 刪表 drop table if exists xiangwan;-- 建表,source、target、cost是pgrouting必需的字段 -- 拉跨帕瓦,頂碗人在哪里? create table xiangwan ( id int, roadName varchar, region geometry, source int, target int, cost float )-- 創建索引,不然巨慢 create index if not exists pgr_source_idx on xiangwan("source"); create index if not exists pgr_target_idx on xiangwan("target");-- 導入數據 insert into xiangwan ("id", "roadname", "region") values ('1', '我愛向晚1', 'LINESTRING(116.403245 39.927884,116.403317 39.926542)'); insert into xiangwan ("id", "roadname", "region") values ('2', '我愛向晚2', 'LINESTRING(116.403317 39.926542,116.400352 39.924245)'); insert into xiangwan ("id", "roadname", "region") values ('3', '我愛向晚3', 'LINESTRING(116.403317 39.926542,116.406676 39.925186)'); insert into xiangwan ("id", "roadname", "region") values ('4', '我愛向晚4', 'LINESTRING(116.400352 39.924245,116.403658 39.920856)'); insert into xiangwan ("id", "roadname", "region") values ('5', '我愛向晚5', 'LINESTRING(116.406676 39.925186,116.403658 39.920856)'); insert into xiangwan ("id", "roadname", "region") values ('6', '我愛向晚6', 'LINESTRING(116.403658 39.920856,116.403586 39.919113)');-- 計算更新權值,權值的計算方法有很多種,這里就取線的距離 update xiangwan set cost = st_length(region) where cost is null;-- 更新拓撲圖,執行完畢后,再去看表里的source、target已經根據誤差自動進行編號了。 select pgr_createTopology('xiangwan',0.000001,'region','id','source','target');-- 具體字段說明 -- 參數1:表名 -- 參數2:誤差緩沖值,兩個點的距離在這個距離內,就算重合為一點。這個距離使用st_length計算 -- 參數3:該表的空間坐標字段 -- 參數4:該表的主鍵 -- 參數5:空間起點編號 -- 參數6:空間終點編號 -- 參數7:看官方描述,默認true -- 參數8:每次執行都重建拓撲圖,默認false select pgr_createTopology('xiangwan',0.000001,'region','id','source','target',rows_where := 'true', clean := 'true');-- 求最短路徑 -- 參數1:sql -- 參數2:起點source -- 參數3:重點target select * from pgr_dijkstra('select id, source, target, cost from xiangwan where roadname like $$%我愛向晚%$$',1,6 );postgresql 單引號的轉義符是$$
上面的寫法就是select id, source, target, cost from xiangwan where roadname like ‘%我愛向晚%’
根據查詢結果可知,經過的點號依次是1->2->3->5->6。
三、實際做法
因為每有數據來了之后,就要對拓撲圖進行一次維護(對cost、source、target的維護)。
由于數據不常變化,這邊就想直接使用postgresql的觸發器實現。更主要的還是我懶得寫代碼維護了!
postgresql的觸發器比較反人類,必須要基于一個觸發器函數才行。這點跟mysql相比,從人性化的角度考慮,簡直被吊打。
-- 刪除函數 drop function updateTopology();-- 創建維護拓撲圖函數,不想輸出內容時,將select換成perform create or replace function updateTopology()returns trigger as $body$ begin update xiangwan set cost = st_length(region) where cost is null; perform pgr_createtopology('xiangwan',0.000001,'region','id','source','target'); return null; end; $body$ language plpgsql;-- 創建觸發器 create trigger maintainTopology after insert or update of region on xiangwanfor each statementexecute procedure updateTopology();-- 刪除觸發器 drop trigger if exists maintainTopology on xiangwan;上面的那個update xiangwan where cost is null,這么寫其實有問題。
但是我的目的是只要有路徑能查出來就行,所以這邊特別精準是哪條路徑對我作用不大。
而且,如果不加where cost is null,就會出現無限套娃的情況!
每次update之后,都要重新執行觸發器。
總結
以上是生活随笔為你收集整理的PgRouting求解大数据量最短路径的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 获取CPU序列号的Delphi程序
- 下一篇: 长白山项目开发小组,day1