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ó. Elődje volt a pgDijkstra, amelyet később kiterjesztettek és pgRouting-nak neveztek el. Napjainkban a fenntartását a Georepublic, az iMaptools és a felhasználók végzik.
OSGeo
A pgRouting az OSGeo Foundation-nek egy OSGeo Labs projektje és része az OSGeo Live-nak, ami egy hordozható szoftver, mellyel ki széles körben ki lehet próbálni térinformatikai szoftvereket installálás nélkül. Teljesen ingyenes, szabadon használható, módosítható és dokumentációt, valamint mintaadatokat is tartalmaz.
A pgRoutingot sokféle módon tudjuk használni, adatmódosítást is több különböző szoftverrel is végezhetünk, pl. pgAdmin, QGIS használatával, vagy konkrét Pl/pgSQL utasítások használatával. Az adatmódosításokat a routing engine azonnal elvégzi és a "cost" paraméter kiszámítása is működik dinamikusan.
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. Természetesen különböző platformokra is elérhető a csomag: Linux, MAC OS és Windows alá is.
Én a Windows verziót választottam. A pgRouting honlapján lévő hivatalos telepítési útmutató alapján hosszas, bonyodalmas telepítésre számítottam, de meglepően egyszerűnek bizonyult. Nem kell más, csak egy PostgreSQL és a hozzá tartozó, megfelelő verziójú PostGIS, amelyet a kapcsolódó StackBuilder-rel automatikusan, könnyen telepíthetünk.
Elérhetőek más extension-ök is, én csak a PostGIS-pgRouting kombót választottam.
Telepítés után az úgy nevezett pgAdmin alkalmazást kell elindítani és ebben kell csatlakozni az adatbázisunkhoz. Az adatbázis futhat localhost-on, sőt, egy kezdeti adatbázist már a PostGIS telepítése során létrehozhatunk.
Az adatbázis jelszóval védett és különböző felhasználókat is létre lehet benne hozni, majd a táblákról megmondani, kiknek van hozzá jogosultsága - tehát itt semmi meglepetés nincs.
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
A 2.3 pgRouting library tartalma/leírása itt található.
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 (hasonló hozzá az osm2pgrouting is, de ez sajnos egy ideje Windows alatt nem működik).
- shp2pgsql: PostgreSQL shapefile betöltője (az újabb verziójához GUI is tartozik, mellyel könnyen lehet shapefile-okat betölteni).
- 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.
Előfordulhat olyan eset is, amikor a pl. shapefile-ból betöltött táblánkon nem tudunk automatikusan topológiát építeni, mivel csak egy "geometry" típusú oszlopból tudjuk, hol van az elhelyezkedése, azaz nem tudjuk a start és az end csúcsát. Szerencsére erre is van megoldás:
1. Létrehozunk egy view-t, amelyikbe kiszámoljuk az adott geom start és end csúcsát (ebben az esetben a road táblán hajtjuk végre az átalakítást).
CREATE OR REPLACE VIEW road_ext AS
SELECT *, startpoint(the_geom), endpoint(the_geom)
FROM road;
2. Létrehozunk egy táblát, amelyben az egyes csomópontokat tároljuk, és ezeknek adunk egy unique ID-t is.
CREATE TABLE node AS
SELECT row_number() OVER (ORDER BY foo.p)::integer AS id,
foo.p AS the_geom
FROM (
SELECT DISTINCT road_ext.startpoint AS p FROM road_ext
UNION
SELECT DISTINCT road_ext.endpoint AS p FROM road_ext
) foo
GROUP BY foo.p;
3. Ahhoz, hogy bejárható hálózatot kapjunk, létrehozunk egy táblát az eredeti és a csomópontok összejoin-olásával.
CREATE TABLE network AS
SELECT a.*, b.id as start_id, c.id as end_id
FROM road_ext AS a
JOIN node AS b ON a.startpoint = b.the_geom
JOIN node AS c ON a.endpoint = c.the_geom;
4. Majd, ha akarjuk létrehozunk spatial index-et a lekérdezések hatékonyabb végrehajtása érdekében.
CREATE INDEX road_gist ON road USING GIST (the_geom);
5. Már mehetnek is a lekérdezések!
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.
A Kétirányú 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) úgy, hogy elkezdi megkeresni a legrövidebb utat a startcsúcsból és párhuzamosan a célcsúcsból is. Akkor ér véget, ha a két keresés valahol középen véget ér. 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_bdDijkstra(sql text, source integer, target integer,
directed boolean, has_rcost boolean);
/*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_bdDijkstra(
'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_bdDijkstra(
'SELECT id, source, target, cost, reverse_cost FROM edge_table',
7, 12, true, true
);
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 k-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), vagy összeadja a költségeket (attól függ, melyik verziót használjuk). 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_kdijkstraCost(text sql, integer source,
integer[] targets, boolean directed, boolean has_rcost); -- az összeköltséggel tér vissza
pgr_costResult[] pgr_kdijkstraPath(text sql, integer source,
integer[] targets, boolean directed, boolean has_rcost); -- az útban lévő élek halmazával tér vissza
/*Ezekben a source a startcsúcs azonosítója,
a targets a célcsúcsok azonosítójának tömbje,
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.*/
k-Dijkstra algoritmussal kiegészítve az előző lekérdezés úgy, hogy az úttal tér vissza:
SELECT seq, id1 AS path, id2 AS edge, cost FROM pgr_kdijkstraPath(
'SELECT id, source, target, cost FROM edge_table WHERE cost >= 0',
10, array[4,12], false, false
);
Költséggel tér vissza:
Returning a cost result
SELECT seq, id1 AS source, id2 AS target, cost FROM pgr_kdijkstraCost(
'SELECT id, source, target, cost FROM edge_table WHERE cost >= 0',
10, array[4,12], false, false
);
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.
Az A* 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). Az algoritmus Dijkstra algoritmuson alapszik, heurisztikus kikötéssel, amely hatékonyabbá teszi. 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 (az utolsó sorra 0).
pgr_costResult[] pgr_astar(sql text, source integer, target integer,
directed boolean, has_rcost boolean);
/*Ezekben 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, x1, y1, x2, y2 [,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,
x1 az él start pontjának x koordinátája,
y1 az él start pontjának y koordinátája,
x2 az él end pontjának x koordinátája,
y2 az él end pontjának y koordiná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.*/
A* algoritmussal kiegészítve az előző lekérdezés reverse_cost nélkül:
SELECT * FROM pgr_AStar(
'SELECT id::INTEGER, source::INTEGER, target::INTEGER, cost, x1, y1, x2, y2 FROM edge_table',
4, 1, false, false);
Reverse_cost-tal:
SELECT * FROM pgr_AStar(
'SELECT id::INTEGER, source::INTEGER, target::INTEGER, cost, x1, y1, x2, y2, reverse_cost FROM edge_table ',
4, 1, true, true);
Kétirányú A* 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.
A Kétirányú A* 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) úgy, hogy elkezdi megkeresni a legrövidebb utat a startcsúcsból és párhuzamosan a célcsúcsból is. Akkor ér véget, ha a két keresés valahol középen véget ér. 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_bdAstar(sql text, source integer, target integer,
directed boolean, has_rcost boolean);
/*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, x1, y1, x2, y2 [,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,
x1 az él start pontjának x koordinátája,
y1 az él start pontjának y koordinátája,
x2 az él end pontjának x koordinátája,
y2 az él end pontjának y koordinátája,
a reverse_cost opcionális, akkor lesz használva, ha a directed és a has_rcost értéke is igaz.*/
Kétirányú A* algoritmussal kiegészítve az előző lekérdezés reverse_cost nélkül:
SELECT * FROM pgr_bdAStar(
'SELECT id::INTEGER, source::INTEGER, target::INTEGER, cost, x1, y1, x2, y2
FROM edge_table',
4, 10, false, false);
Reverse_cost-tal:
SELECT * FROM pgr_bdAStar(
'SELECT id::INTEGER, source::INTEGER, target::INTEGER, cost, x1, y1, x2, y2, reverse_cost
FROM edge_table ',
4, 10, true, true);
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 .
A Yen algoritmus pgRouting lekérdezéseket használva:
Az algoritmus pgr_costResult(seq, id1, id2, id3, cost) sorok halmazával tér vissza, melyek utakat hoznak létre (a legrövidebb utat a két csúcs között) összesen az első k darabot. Ebben a seq a sorok szekvenciája, az id1 az út azonosítója, az id2 a csúcs azonosítója, az id3 az él azonosítója (az utolsó sorban 0) a cost az áthaladás költsége id2-től id3-at használva.
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.*/
Yen algoritmussal kiegészítve az előző lekérdezés reverse_cost nélkül:
SELECT seq, id1 AS route, id2 AS node, id3 AS edge, cost
FROM pgr_ksp(
'SELECT id, source, target, cost FROM edge_table',
7, 12, 2, false
);
Reverse_cost-tal:
SELECT seq, id1 AS route, id2 AS node, id3 AS edge, cost
FROM pgr_ksp(
'SELECT id, source, target, cost, reverse_cost FROM edge_table',
7, 12, 2, true
);
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 .
A Floyd-Warshall 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 súlyozott gráfban, mindegyiknél kiszámolva a legrövidebb utat:
pgr_costResult[] pgr_aspsWarshall(sql text, directed boolean, reverse_cost boolean); -- 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,
a reverse_cost ha igaz, akkor a cost értékét az ellentétes irányból vesszük.*/
Floyd-Warshall algoritmussal kiegészítve az előző lekérdezés:
SELECT seq, id1 AS from, id2 AS to, cost
FROM pgr_apspWarshall(
'SELECT id, source, target, cost FROM edge_table',
false, false
);
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.
A vezetési távolság algoritmus pgRouting lekérdezéseket használva:
Az algoritmus pgr_costResult(seq, id1, id2, id3, cost) egy seq-id párossal tér vissza, amely azt írja le, mi a legjobb haladási sorrend a mátrixban, hogy eljussunk a start csúcsból a meghatározott csúcsokig. Ebben a seq a sorok szekvenciája, az id1 az út azonosítója, az id2 a csúcs azonosítója, az id3 az él azonosítója (az utolsó sorban 0) a cost az áthaladás költsége id2-től id3-at használva.
pgr_costResult[] pgr_tsp(sql text, ids varchar, source integer);
Utazó ügynök algoritmus SQL paraméterrel:
SELECT * FROM pgr_tsp('SELECT id AS source_id, x, y FROM vertex_table','2,7,11',7);
Távolsági mátrixszal:
SELECT seq, id FROM pgr_tsp('{{0,1,2,3},{1,0,3,2},{2,3,0,4},{3,2,4,0}}',2);
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.
A legrövidebb út kanyarodási korlátokkal algoritmus pgRouting lekérdezéseket használva:
Az algoritmus pgr_costResult(seq, id1, id2, id3, cost) sorok halmazával tér vissza, minden sor egy élt tartalmaz, amelyiken átmentünk, plusz az utolsó az utolsó élt. Ebben a seq a sorok szekvenciája, az id1 az út azonosítója, az id2 a csúcs azonosítója, az id3 az él azonosítója (az utolsó sorban 0) a cost az áthaladás költsége id2-től id3-at használva.
Segítségül egy olyan SQL lekérdezést használunk, amelyiknek a vizsgált gráf éleivel kell visszatérnie, attól függően, hogy van-e megszorítás:
pgr_costResult[] pgr_trsp(sql text, source integer, target integer, directed boolean, has_rcost boolean [,restrict_sql text]);
pgr_costResult[] pgr_trsp(sql text, source_edge integer, source_pos double precision, target_edge integer, target_pos double precision, directed boolean, has_rcost boolean [,restrict_sql text]);
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.*/
TSRP algoritmussal kiegészítve az előző lekérdezés megszorítások nélkül:
SELECT seq, id1 AS node, id2 AS edge, cost
FROM pgr_trsp(
'SELECT id, source, target, cost FROM edge_table',
7, 12, false, false
);
Megszorításokkal (külön táblában tároljuk):
SELECT seq, id1 AS node, id2 AS edge, cost
FROM pgr_trsp(
'SELECT id, source, target, cost FROM edge_table',
7, 12, false, false,
'SELECT to_cost, to_edge AS target_id,
from_edge || coalesce('','' || via, '''') AS via_path
FROM restrictions'
);
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.
A vezetési távolság algoritmus pgRouting lekérdezéseket használva:
Az algoritmus pgr_costResult(seq, id1, id2, id3, cost) sorok halmazával tér vissza, minden sor egy élt tartalmaz, amelyiken átmentünk, plusz az utolsó az utolsó élt. Ebben a seq a sorok szekvenciája, az id1 az út azonosítója, az id2 a csúcs azonosítója, az id3 az él azonosítója (az utolsó sorban 0) a cost az áthaladás költsége id2-től id3-at használva.
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.*/
Vezetési távolság algoritmussal kiegészítve az előző lekérdezés:
SELECT * FROM pgr_drivingDistance(
'SELECT id, source, target, cost, reverse_cost FROM edge_table',
2, 3
);
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.