PgRouting
A pgRouting egy nyílt forráskódú, C++ nyelven írt bővítmény a PostGIS/PostgreSQL térinformatikai adatbázisokhoz. Lehetővé teszi gráf adatszerkezetek kezelését, így például legrövidebb utakat, csomópontok távolságát és egyéb hálózati elemzést igénylő feladatokat oldhatunk meg vele. A programcsomag GNU GPL v2 licenc alatt használható.
Tartalomjegyzék
Telepítés
A PostGIS újabb verziói (2.0+) már tartalmazzák a pgRouting csomagot, így nem szükséges külön telepíteni, csak aktiválni kell, a következő módon.
CREATE EXTENSION pgrouting;
A teljes telepítési útmutató elérhető a pgRouting honlapján.
Felépítés
A pgRouting két fő összetevőből áll:
- Egy C modul, amely egy PostgreSQL-ben átadott lekérdezést használ a gráf felépítéséhez.
- C++ modulok, amelyek a lekérdezést úgynevezett boost gráffá alakítják, és futtatják az útvonal-választást.
Hogy mely összetevőt használja a program futása során, az az algoritmus fajtájától függ.
A függvénykönyvtár szerkezete pedig a következőképp néz ki:
cmake/ - cmake fájlok CMakeLists.txt - Top level cmake doc/ - Top level doc, a nem forrásalgoritmus-specifikus dokumentáció helye themes/ - Sphinx téma a doc-nak static/ - dokumentáció képei src/ astar/ - A* algoritmus common/ - pgRouting projektekben szükséges közös fájlok dijkstra/ - Dijkstra algoritmus driving_distance/ - Vezetési távolság trsp/ - Legrövidebb utak kanyarodási korlátokkal tsp/ - Utazó ügynök tools/ - Tesztelési eszközök
Az src mappa minden könyvtárában doc
, sql
, src
, és test
almappák találhatók, melyekbe értelemszerűen a következők kerülnek:
- doc: Az adott algoritmushoz szükséges összes dokumentáció ReStructuredText formátumban. Ennek célja, hogy a függvények leírásával, input és output paraméterek megadásával a felhasználók számára érthetővé tegye, hogyan működik az algoritmus.
- sql: Az sql burkolók a C kódhoz.
- src: C/C++ kód az algoritmushoz.
- test:
test.conf
,*.data
,*.test
, és*.rest
fájlok az algoritmus teszteléséhez.
Fejlesztési célból a programot klónozhatjuk Git-en keresztül, majd telepítjük a következők szerint:
git clone git@github.com:pgRouting/pgrouting.git
cd pgrouting
cmake -DWITH_TSP=ON -DWITH_DD=ON .
make
sudo make install
Használat
Adatok betöltése
A vektoros térképi adatok adatbázis-táblába betöltésére számos eszköz áll rendelkezésre, például:
- osm2po: OpenStreetMap (OSM) adatok konvertálása SQL formátumba, pgRouting-nak megfelelő formátumban.
- shp2pgsql: PostgreSQL shapefile betöltője.
- ogr2ogr: Vektoros adatok konverziója.
- osm2pgsql: OSM adat betöltése postgreSQL-be.
Előfeldolgozás
Amikor egy GIS fájlt olvasunk be az adatbázisunkba a pgRouting számára, nem feltétlenül követnek alkalmas topológiát a rekordok. A helytelen topológia hibás útvonalakhoz vezetne. Hogy használható adattáblát kapjunk, csomópontokra van szükségünk minden egyes útkereszteződésnél. Az útvonalhálózat megfelelő topológiájának kialakítását segítheti a pgr_createTopology vagy a pgr_nodeNetwork parancs. Működésük hasonló. Az utóbbi beolvassa a „csúcstalan” hálózat adatbázis-tábláját, majd egy új táblába írja a már felcsúcsozott éleket. Paraméterei
- éltábla neve,
- tolerancia: a toleranciaértéken belüli csomópontok egy csomópontot fognak alkotni,
- id, az éltábla elsődleges kulcsa,
- geometriát tartalmazó oszlop,
- kimeneti tábla szuffixe. Alapértelmezetten edge_table_noded.
Lekérdezés szintaxis
Minden pgRouting lekérdezés az alábbi formátumot követi:
SELECT pgr_<algorithm>(<SQL for edges>, start, end, <additonal options>)
ahol a pgr_
előtaggal hívjuk meg a lentebb ismertetett algoritmusok egyikét. A belső SQL lekérdezéssel adjuk meg a táblát, amelyre az algoritmust futtatjuk, a kezdő- és végponto(ka)t, valamint opcionális további szűrési feltételeket. Például, ha a pgRouting dokumentációjában bemutatott mintaadatokkal dolgozunk, az alábbi lekérdezéssel a 2. sorszámú csúcsból a 3. sorszámúba vezető legrövidebb utat kapjuk meg:
SELECT * FROM pgr_dijkstra(
'SELECT id, source, target, cost, reverse_cost
FROM edge_table', 2, 3);
Algoritmusok
Dijkstra algoritmus
A Dijkstra algoritmus egy súlyozott élű, irányított gráf adott csúcsából egy másik adott csúcsába vezető legrövidebb út megtalálására szolgáló módszer. Az algoritmus csak nem negatív élsúlyok esetén működik. Jelölje a súlyfüggvényt . Az algoritmus mohó módszert használ. Legyen a kiindulási pont , a célállomás pedig . Az algoritmus egy halmazban tárolja azon csúcsokat, amelyeknek már ismerjük az -ből hozzájuk vezető legrövidebb út hosszát. Ezen kívül minden csúcsról nyilvántartunk az algoritmus futása során egy értéket, ami az -ből -ba vezető addig megismert legrövidebb út hossza.
Kezdetben üres, és minden más csúcsra . Ezután minden iterációban kiválasztunk a halmazból egy olyan csúcsot, amelyre minimális, áttesszük -et -ba, majd minden olyan szomszédjára, amely nincs -ban frissítjük a értéket: . Az algoritmus véget ér, amikor átkerül -ba, ekkor egy legrövidebb -ből -be vezető út hossza. Az algoritmus kis módosítással egy legrövidebb utat is megad -ből -be.
Az algoritmus költsége szomszédsági mátrixos gráfábrázolás esetén , szomszédsági listás gráfábrázolás esetén Fibonacci kupacot használva .
A Dijkstra algoritmus pgRouting lekérdezéseket használva:
Az algoritmus pgr_costResult(seq, id1, id2, cost) sorok halmazával tér vissza, melyek egy utat hoznak létre (a legrövidebb utat a két csúcs között). Ebben a seq a sorok szekvenciája, az id1 a startcsúcs azonosítója, az id2 az él azonosítója (az utolsó sorban -1), a cost az áthaladás költsége id1-től id2 élen keresztül.
pgr_costResult[] pgr_dijkstra(text sql, integer source, integer target,
boolean directed, boolean has_rcost);
/*Ebben a source a startcsúcs azonosítója,
a target a célcsúcs azonosítója,
a directed akkor igaz, ha az él irányított,
a has_rcost akkor igaz, ha negatív élek vannak, ekkor a kapott sorokat az algoritmus visszafelé dolgozza fel.*/
Segítségül egy olyan SQL lekérdezést használunk, amelyiknek a vizsgált gráf éleivel kell visszatérnie:
SELECT id, source, target, cost [,reverse_cost] FROM edge_table
/* Ebben az id az él azonosítója,
a source: az él startcsúcsának azonosítója,
a target az él célcsúcsának azonosítója,
a cost egy pozitív szám, az élen való áthaladásnak a költsége,
a reverse_cost opcionális, akkor lesz használva, ha a directed és a has_rcost értéke is igaz.*/
Dijkstra algoritmussal kiegészítve az előző lekérdezés reverse_cost nélkül:
SELECT seq, id1 AS node, id2 AS edge, cost
FROM pgr_dijkstra(
'SELECT id, source, target, cost FROM edge_table',
7, 12, false, false
);
Reverse_cost-tal:
SELECT seq, id1 AS node, id2 AS edge, cost
FROM pgr_dijkstra(
'SELECT id, source, target, cost, reverse_cost FROM edge_table',
7, 12, true, true
);
Jól látható, hogy a két módszer csak a directed és a has_rcost értékekben különbözik.
Kétirányú Dijkstra algoritmus
A kétirányú Dijkstra algoritmus szintén egy súlyozott élű, irányított gráf adott csúcsából egy másik adott csúcsába vezető legrövidebb út megtalálására szolgáló módszer. Az algoritmus csak nem negatív élsúlyok esetén működik. Legyen a kiindulási pont , a célállomást pedig . A Dijkstra algoritmust futtatjuk a kiindulási pontból az eredeti gráfon, és párhuzamosan a célállomásból a transzponált gráfon (amelyet úgy kapunk az eredeti gráfból, hogy az élek irányítását megfordítjuk). Akkor állunk meg, ha egy csúcs mindkét irányból bekerül a Dijkstra algoritmusnál definiált halmazba (pontosabban a és a halmazba is). Egy alternatív lehetőség, hogy a és a halmazok közül minden egyes iterációban csak azt bővítjük, amelyiknél kisebb. Az algoritmus kis módosítással egy legrövidebb utat is megad -ből -be.
k-Dijkstra algoritmus
A -Dijkstra algoritmus egy súlyozott élű, irányított gráf adott csúcsából több adott csúcsába vezető legrövidebb út megtalálására szolgáló módszer. Valójában a Dijkstra algoritmus a gráf egy adott csúcsából az összes többi csúcsba vezető legrövidebb utat megtalálja, így akárhány célállomást megadhatunk. Az algoritmus akkor fejeződik be, amikor minden egyes célállomás bekerült a Dijkstra algoritmusnál definiált halmazba. Az algoritmus kis módosítással magukat a legrövidebb utakat is megadja.
Az algoritmus költsége szomszédsági mátrixos gráfábrázolás esetén , szomszédsági listás gráfábrázolás esetén Fibonacci kupacot használva .
A* algoritmus
Az algoritmus egy súlyozott élű, irányított gráf adott csúcsából egy másik adott csúcsába vezető legrövidebb út megtalálására szolgáló módszer. Az algoritmus a Dijkstra algoritmus általánosítása, és szintén csak nem negatív élsúlyok esetén működik. Jelölje a súlyfüggvényt . Az algoritmus mohó módszert használ. Legyen a kiindulási pont , a célállomás pedig . Az algoritmus egy halmazban tárolja azon csúcsokat, amelyeknek már ismerjük az -ből hozzávezető legrövidebb út hosszát, egy halmazban pedig azokat, amelyeket már elértünk -ből, de az -ből hozzájuk vezető legrövidebb út hosszát még nem ismerjük. Minden csúcsról nyilvántartunk az algoritmus futása során három értéket: az -ből -ba vezető addig megismert legrövidebb út hossza, egy nem negatív heurisztikus alsó becslés az -ból -be vezető legrövidebb út hosszára, amelyre az is teljesül, hogy az -ból bármely csúcsba vezető legrövidebb út hosszára alsó korlát (például egy térkép esetén és légvonalban mért távolsága), végül .
Kezdetben üres, -ben csak az csúcs van, és . Ezután minden iterációban kiválasztunk az halmazból egy olyan csúcsot, amelyre minimális, áttesszük -et -ba, majd minden olyan szomszédjára, amely nincs -ban frissítjük először a értéket: , majd a értéket: , végül áttesszük -t -be. Az algoritmus véget ér, amikor átkerül -ből -ba, ekkor egy legrövidebb -ből -be vezető út hossza. Az algoritmus kis módosítással egy legrövidebb utat is megad -ből -be.
A tapasztalat azt mutatja, hogy az algoritmus számos esetben hatékonyabb, mint a Dijkstra algoritmust.
Kétirányú algoritmus
A kétirányú algoritmus egy súlyozott élű, irányított gráf adott csúcsából egy másik adott csúcsába vezető legrövidebb út megtalálására szolgáló módszer. Az algoritmus csak nem negatív élsúlyok esetén működik. Legyen a kiindulási pont , a célállomást pedig . Az algoritmust futtatjuk a kiindulási pontból az eredeti gráfon, és párhuzamosan a célállomásból a transzponált gráfon (amelyet úgy kapunk az eredeti gráfból, hogy az élek irányítását megfordítjuk). Akkor állunk meg, ha egy csúcs mindkét irányból bekerül az algoritmusnál definiált halmazba (pontosabban a és a halmazba is). Az algoritmus kis módosítással egy legrövidebb utat is megad s-ből t-be.
Yen algoritmus
A Yen algoritmus egy súlyozott élű, irányított gráf egy adott csúcsából egy adott másik csúcsába vezető legrövidebb út mellett megtalálja a második, harmadik, …, -adik legrövidebb (egyszerű) utat is. Az algoritmus csak nem negatív élsúlyok esetén működik.
Az algoritmus költsége .
Floyd-Warshall algoritmus
A Floyd-Warshall algoritmus egy súlyozott élű, irányított gráf bármely két csúcsa közötti legrövidebb út megtalálására szolgáló módszer. Az algoritmus negatív élsúlyok esetén is működik, ha a gráf nem tartalmaz negatív összhosszúságú irányított kört. Jelölje a súlyfüggvényt . Az algoritmus dinamikus programozást használ. Legyen a gráf csúcshalmaza . Az algoritmus minden esetén meghatározza a -ből -be menő legrövidebb olyan (egyszerű) út hosszát, amelyen a közbülső csúcsok a halmazból kerülnek ki: , és ha . Az algoritmus kis módosítással magukat a legrövidebb utakat is megadja.
Az algoritmus költsége .
Johnson algoritmus
A Johnson algoritmus egy súlyozott élű, irányított gráf bármely két csúcsa közötti legrövidebb út megtalálására szolgáló módszer. Az algoritmus negatív élsúlyok esetén is működik, ha a gráf nem tartalmaz negatív összhosszúságú irányított kört. Jelölje a súlyfüggvényt . Az algoritmus ritka gráfokon teljesít igazán jól.
Vegyünk fel egy új csúcsot, és vezessünk -ből nulla súlyú éleket csúcsaiba. Jelölje az így kapott gráfot . Futtassuk le -re a Bellman-Ford algoritmust az kezdőcsúccsal. Az algoritmus által szolgáltatott távolságérték a gráf egy csúcsára legyen . Átsúlyozzuk a gráf éleit: legyen minden élre. Most egy nem negatív súlyfüggvény -n. Futtassuk ezzel a súlyfüggvénnyel a Dijkstra algoritmust a gráf minden csúcsából. Egy legrövidebb -ból -be vezető út hossza ezután a kapott érték mínusz . Az algoritmus kis módosítással magukat a legrövidebb utakat is megadja.
Az algoritmus költsége szomszédsági listás gráfábrázolás esetén Fibonacci kupacot használva .
A Johnson algoritmus pgRouting lekérdezéseket használva:
Az algoritmus pgr_costResult(seq, id1, id2, cost) sorok halmazával tér vissza minden csúcspárra a gráfban:
pgr_costResult[] pgr_aspsJohnson(sql text); -- magyarul minden egyes csúcspárra kiszámolja az összes költséget.
/*Ebben a seq a sorok szekvenciája,
az id1 a startcsúcs azonosítója,
az id2 a célcsúcs azonosítója,
a cost az id1-től id2-ig jutás költsége.*/
Segítségül egy olyan SQL lekérdezést használunk, amelyiknek a vizsgált gráf éleivel kell visszatérnie:
SELECT source, target, cost FROM edge_table;
/* Ebben a source: az él startcsúcsának azonosítója,
a target az él célcsúcsának azonosítója,
a cost egy pozitív szám, az élen való áthaladásnak a költsége.*/
Johnson algoritmussal kiegészítve az előző lekérdezés:
SELECT seq, id1 AS from, id2 AS to, cost
FROM pgr_apspJohnson(
'SELECT source, target, cost FROM edge_table'
);
Utazó ügynök probléma
Az utazó ügynök probléma a következő. Adott városoknak egy listája, és adott bármely két város között a távolság. Határozzunk meg egy olyan körutat, amely minden városon pontosan egyszer halad át, és a hossza minimális. A problémára nem ismert polinomiális költségű algoritmus. A program ún. simulated annealing technikán alapuló algoritmust használ egy jó közelítő megoldás meghatározására.
Legrövidebb utak kanyarodási korlátokkal
Az algoritmus egy súlyozott élű, irányított gráf adott csúcsából egy másik adott csúcsába vezető legrövidebb út meghatározásakor képes figyelembe venni, hogy két csatlakozó él egymás utáni bejárása extra költséggel bírhat. Ezeket a megszorításokat egy külön táblában adhatjuk meg. Tapasztalat szerint az algoritmus közel olyan gyors, mint az algoritmus.
Vezetési távolság
Az algoritmus egy súlyozott élű, irányított gráfban a Dijkstra algoritmus felhasználásával megadja azon csúcsokat, melyekbe egy vagy több adott csúcsból egy adott értéknél rövidebb úton el lehet jutni.
Kapcsolódó alkalmazások
Útvonal-meghatározásra alkalmas adathalmazt például OpenStreetMap (OSM) térképekből nyerhetünk ki. Ehhez egyik alkalmas segédeszköz az OSM2PO alkalmazás, amely megfelelő topológiájú SQL fájlt állít elő a megadott térképrészlethez, amely egyből alkalmas a pgRouting-gal vagy QGIS-szel való feldolgozásra.
Az OSM2PO révén vizualizálhatjuk is a legrövidebb utakat. A programmal előállított SQL táblákban minden útkereszteződéshez tartozik egy rekord, melyben a következőket tároljuk:
- koordináták (
x1, x2, y1, y2
), - név (
osm_name
), - csomópont azonosító (
id
), - lehetséges továbbhaladási irány (
osm_source_id, osm_target_id
), - útszakasz hossza (
km
), - megengedett sebesség (
kmh
), - költség (
cost
), - geometria (
geom_way
).
A demo.bat
fájl szerkesztésével specifikálhatjuk térképünk paramétereit, például, hogy mely térképrészleten dolgozzunk. A Mapzen oldalán kész .pbf
formátumú városrészleteket találhatunk, melyeket kompatibilisek az Osm2Po-val. A fájl futtatásával előáll egy .sql
fájl is, melyet importálhatunk a postGIS adatbázisunkba, és akár pgRouting lekérdezéseket is futtathatunk rajta.
Amíg fut a program, helyi webszerveren (localhost:8888/Osm2poService
) jeleníthető meg az importált térképrészlet, melyen az útkeresést is kipróbálhatjuk.
Szakirodalom
- Regina O. Obe, Leo S. Hsu. "pgRouting: A Practical Guide", Locate Press, 2016.
- Regina O. Obe, Leo S. Hsu. "PostGIS in Action (Second Edition)", Manning Publictions (ISBN 9781617291395). 2015. pp 286-290.
- Lijing Zhang, Xuanhui He. "Route Search Base on pgRouting", Software Engineering and Knowledge Engineering: Theory and Practice Vol 2.. Springer-Verlag Berlin Heidelberg, 2012. pp 1003-1007.