[pgrouting-users] driving directions

lorenzo amato lorenzo.amato at geosdi.org
Mon Jan 24 05:02:50 EST 2011


Hi Emre, hi list

I faced the Driving Direction issue and I wrote a pl/pgsql function.

It is only an example and it is not optimized. There are some comments in
italian language (I'm sorry .. I didn't have the time to translate them).

It assume the name of the street table as "ways", use the SPS algorithm and
returns a xml containing the Driving Directions and the geometry of the
route.

If you want to see it in action you can visit the demo at
http://routing.geosdi.org (also mentioned in the pgRouting Gallery - thanks
to Daniel)

Some information about the algorithm:

the routing query returns the geometries of the edges composing the shortest
path, but they are not oriented. So the first things to do is to analyze
every couple ov edge and flip the segments if necessary, so that the final
point of the n_th segment coincides with the start point of the n+1_th
segment.
than, using the azimuth, evaluate the angle between two consecutive segments
to define the maneuver to suggest.

This is far to be used in a production environment, but it could be an
example to start (I could not find any example when I started to face the DD
issue !!)

Hope this help.


Daniel ... we could post it into the contribution with git after I optimize
it .. How to do this?


Here is the not-optimized function.

-- Function: xml_directions(integer, integer, text)

-- DROP FUNCTION xml_directions(integer, integer, text);

CREATE OR REPLACE FUNCTION xml_directions(source_id integer, target_id
integer, tab_name text)
  RETURNS xml AS
$BODY$DECLARE

start_point geometry; --geometria del punto iniziale
tab_name text :=$3; -- nome della tabella (ways)
s_id integer :=$1; -- source id
t_id integer :=$2; -- target id
_first boolean :=true; --è il primo step dell'algoritmo
fc real:= 111073.2835905824; -- fattore conversione moltiplicativo per le
distanze
_A geometry;
_B geometry;
_C geometry;
_D geometry;
a1 float; -- azimuth fine prima geometria
a2 float; -- azimuth inizio seconda geometria
angoli real[]; -- array degli angoli (differenza) da restituire
i integer; -- intero per scorrere l'array - NB è definito fuori dal loop
perchè serve anche per ottenere la lunghezza degli array

the_route CURSOR FOR SELECT rt.gid, rt.the_geom, length(rt.the_geom),
ways.name FROM ways, (SELECT gid, the_geom FROM shootingstar_sp(tab_name,
s_id, t_id, 0.1, 'cost', true, true)) AS rt WHERE ways.gid=rt.gid;
--variabili per il fetch
GID_1 integer;
GID_2 integer;
GEOM_1 geometry;
GEOM_2 geometry;
LENGTH_1 real;
LENGTH_2 real;
NAME_1 text;
NAME_2 text;




directions text[]; --vettore contenente i codici relaivi alle manovre
lengths real[]; --vettore contenente le lunghezze dei tratti da percorrere
streets text[]; --vettore contenente i nomi dele strade della rotta
geoms geometry[]; --vettore delle singole geometrie
line geometry; --geometria complessiva della rotta
tot_len real :=0;
tot_time real:=0;
tlu text := 'm'; -- total length units

-- var per l'execute della query per la composizione dell'xml
pre_string text;


steps_fragment text:='XMLELEMENT(NAME step,XMLELEMENT(NAME text,''Start from
';
line_fragment text;

xml_result xml;

BEGIN

angoli[0] :=0; -- la prima indicazione è sempre "procedi su via ..." . Il
riempimento del vettore angoli deve partire da angoli[1]
i:=0;
OPEN the_route;
FETCH the_route INTO GID_1, GEOM_1, LENGTH_1, NAME_1;
 IF NAME_1 IS NOT NULL THEN
streets[0]:= replace(trim(both from NAME_1),'''','''''');
ELSE streets[0]:='Missing Street Name';
END IF;
 lengths[0]:= LENGTH_1*fc;
--tot_len := tot_len + lengths[0];
line:=ST_Union(GEOM_1);
RAISE NOTICE ' STREET [0] = %',streets[0];
RAISE NOTICE ' LENGTHS [0] = %',lengths[0];
LOOP
FETCH the_route INTO GID_2, GEOM_2, LENGTH_2, NAME_2;
EXIT WHEN NOT FOUND;

-- caso primo segmento
 IF _first THEN
-- devo capire chi è il punto iniziale
_A :=ST_PointN(GEOM_1,1);
_B :=ST_PointN(GEOM_1,ST_NumPoints(GEOM_1));
_C :=ST_PointN(GEOM_2,1);
_D :=ST_PointN(GEOM_2,ST_NumPoints(GEOM_2));
IF (ST_Equals(_A, _C)) THEN
start_point := _B;
 ELSIF (ST_Equals(_A, _D)) THEN
start_point := _B;
 ELSE
start_point := _A;
 END IF;
 --valuto quale endpoint della prima geometria è connessa allo startpoint e
se necessario flippo la geometria
IF NOT(ST_Equals(start_point,ST_PointN(GEOM_1,1))) THEN -- lo start point
non coincide con il primo punto del primo segmento, quindi il segmento va
flippato
GEOM_1 := ST_Reverse(GEOM_1);
 END IF;
geoms[i]:= GEOM_1;
 start_point:= ST_PointN(Geom_1,ST_NumPoints(GEOM_1)); --aggiorno lo start
point come l'ultimo punto della prima geometria

--valuto quale endpoint della seconda geometria è connessa all'ultimo punto
della prima geometria (che è già stata correttamente orientata) e se
necessario flippo la seconda geometria
IF NOT(ST_Equals(start_point, ST_PointN(GEOM_2,1))) THEN
GEOM_2 := ST_Reverse(GEOM_2);
 END IF;
start_point:= ST_PointN(Geom_2,ST_NumPoints(GEOM_2)); -- aggiorno lo start
point per lo step successivo del loop

-- i segmenti sono orientati; calcolo gli azimuth e definisco la direzione
a1 :=
trunc(ST_Azimuth(ST_PointN(GEOM_1,ST_NumPoints(GEOM_1)-1),ST_PointN(GEOM_1,ST_NumPoints(GEOM_1)))/(2*pi())*360);
 a2 :=
trunc(ST_Azimuth(ST_PointN(GEOM_2,1),ST_PointN(GEOM_2,2))/(2*pi())*360);
 -- la differenza di questi angoli definisce la manovra da effettuare,
quindi VA RITORNATA IN QUALCHE MODO
angoli[i+1] := (a1 - a2);
IF (angoli[i+1] >180.0) THEN angoli[i+1]:=angoli[i+1]-360;
END IF;
IF (angoli[i+1]< -180) THEN angoli[i+1]:=angoli[i+1]+360;
END IF;
IF NAME_2 IS NOT NULL THEN
streets[i+1]:= replace(trim(both from NAME_2),'''','''''');
ELSE
streets[i+1]:='nome strada mancante';
END IF;
 -- Se le strade hanno lo stesso nome
 IF ((streets[i+1] =streets[i])) THEN
 geoms[i]:=ST_Union(geoms[i],GEOM_2);
lengths[i]:=lengths[i]+(LENGTH_2 * fc);
line:=ST_Union(line,GEOM_2);
GEOM_1 := GEOM_2;
NAME_1 := NAME_2;
 ELSE
lengths[i+1]:= LENGTH_2 * fc;
 geoms[i+1]:= GEOM_2;
line:=ST_Union(line,GEOM_2);

i:= i+1;
 GID_1 := GID_2;
GEOM_1 := GEOM_2;
LENGTH_1 := LENGTH_2 * fc;
NAME_1 := NAME_2;
END IF;
 _first := false;

-- caso segmenti successivi al primo (la prima geometria è già orientata)
ELSE
IF NOT(ST_Equals(start_point, ST_PointN(GEOM_2,1))) THEN
GEOM_2 := ST_Reverse(GEOM_2);
 END IF;
start_point:= ST_PointN(Geom_2,ST_NumPoints(GEOM_2)); -- aggiorno lo start
point per lo step successivo del loop

-- i segmenti sono orientati; calcolo gli azimuth e definisco la direzione
a1 :=
trunc(ST_Azimuth(ST_PointN(GEOM_1,ST_NumPoints(GEOM_1)-1),ST_PointN(GEOM_1,ST_NumPoints(GEOM_1)))/(2*pi())*360);
 a2 :=
trunc(ST_Azimuth(ST_PointN(GEOM_2,1),ST_PointN(GEOM_2,2))/(2*pi())*360);
 -- la differenza di questi angoli definisce la manovra da effettuare,
quindi VA RITORNATA IN QUALCHE MODO
 angoli[i+1] := (a1 - a2);
IF (angoli[i+1] >180) THEN angoli[i+1]:=angoli[i+1]-360;
END IF;
IF (angoli[i+1]< -180) THEN angoli[i+1]:=angoli[i+1]+360;
END IF;

IF NAME_2 IS NOT NULL THEN
streets[i+1]:= replace(trim(both from NAME_2),'''','''''');
ELSE
streets[i+1]:='nome strada mancante';
END IF;

-- Se le strade hanno lo stesso nome
IF ( (streets[i+1] =streets[i])) THEN
 geoms[i]:=ST_Union(geoms[i],GEOM_2);
lengths[i]:=lengths[i]+(LENGTH_2 * fc);
line:=ST_Union(line,GEOM_2);
GEOM_1 := GEOM_2;
NAME_1 := NAME_2;

ELSE


lengths[i+1]:= LENGTH_2 * fc;
--tot_len := tot_len + lengths[i+1];
geoms[i+1]:=GEOM_2;

line:=ST_Union(line,GEOM_2);
 i:= i+1;
--GID_1 := GID_2;
GEOM_1 := GEOM_2;
--LENGTH_1 := LENGTH_2 * fc;
NAME_1 := NAME_2;
END IF;

END IF;

END LOOP;
  CLOSE the_route;

--codifico gli angoli in manovre
for k in 0..i LOOP
CASE
WHEN angoli[k]>=-5 AND angoli[k]<=5 THEN
directions[k]:='Go straight along';
--RAISE NOTICE'RAMO 1    %',directions[k];
WHEN angoli[k]>=-45 AND angoli[k]<-5 THEN
directions[k]:='Slight right onto';
--RAISE NOTICE'RAMO 2    %',directions[k];
WHEN angoli[k]>=-100 AND angoli[k]<-45 THEN
directions[k]:='Turn right onto';
--RAISE NOTICE'RAMO 3    %',directions[k];
WHEN angoli[k]>=-135 AND angoli[k]<-100 THEN
directions[k]:='bend to the right on';
--RAISE NOTICE'RAMO 4    %',directions[k];
WHEN angoli[k]>=-180 AND angoli[k]<-135 THEN
directions[k]:='U-turn right onto';
--RAISE NOTICE'RAMO 5    %',directions[k];
WHEN angoli[k]>5 AND angoli[k]<=45 THEN
directions[k]:='Slight left onto';
--RAISE NOTICE'RAMO 6    %',directions[k];
WHEN angoli[k]>45 AND angoli[k]<=100 THEN
directions[k]:='Turn left onto';
--RAISE NOTICE'RAMO 7    %',directions[k];
WHEN angoli[k]>100 AND angoli[k]<=135 THEN
directions[k]:='bend to the left on';
--RAISE NOTICE'RAMO 8    %',directions[k];
WHEN angoli[k]>135 AND angoli[k]<=180 THEN
directions[k]:='U-turn left onto';
--RAISE NOTICE'RAMO 9    %',directions[k];
ELSE RAISE NOTICE' ELSE%',directions[k];
END CASE;
tot_len := tot_len + lengths[k];
END LOOP;
tot_len := trunc(tot_len);
tot_time := trunc(tot_len/833.33333); -- si assume velocità media di 50 km/h

IF (tot_len >= 1000) THEN
tot_len := tot_len/1000; --mostro la len in km
tlu := 'km';
END IF;
pre_string:='XMLROOT(XMLELEMENT(NAME result, XMLELEMENT(NAME route,
XMLELEMENT(NAME total_length, ''circa '||tot_len||' '||tlu||'''),
XMLELEMENT( NAME total_estimated_time, ''about '||tot_time||' min''),
XMLELEMENT(NAME directions,';
steps_fragment := steps_fragment||streets[0]||'''))';
for j in 0..i LOOP
--RAISE NOTICE'geometria ------- %/',ST_AsText(geoms[j]);
steps_fragment := steps_fragment||',XMLELEMENT(NAME step,XMLELEMENT(NAME
text,'||''''||directions[j]||' '||streets[j]||' (about
'||trunc(lengths[j])||' m)'||''''||'),XMLELEMENT(NAME
line,'''||ST_AsText(geoms[j])||'''))';
END LOOP;
 line_fragment:='),XMLELEMENT(NAME
complete_line,'''||ST_AsText(line)||'''))),VERSION 1.0,STANDALONE YES)';
--RAISE NOTICE'geometria -----------------------\n
--------------------------------------- \n\n
%\n\n----------------------------------------',ST_AsText(line);

EXECUTE  'SELECT '||pre_string||steps_fragment||line_fragment INTO
xml_result;

RETURN xml_result;

END;

$BODY$
  LANGUAGE 'plpgsql' VOLATILE
  COST 100;
ALTER FUNCTION xml_directions(integer, integer, text) OWNER TO postgres;





2011/1/23 Stephen Woodbridge <woodbri at swoodbridge.com>

> On 1/23/2011 9:22 AM, Emre Koc wrote:
>
>> Hi All,
>>
>> Does any one can provide sample functions or any kind of help on driving
>> directions? I am trying to figure out how to do that but any help would
>> be very helpful.
>>
>> Thank you,
>>
>> Emre
>>
>
> Here is the thread where I described it before:
>
> http://www.google.com/#hl=en&num=100&newwindow=1&q=site%3Ahttp%3A%2F%2Flists.postlbs.org%2Fpipermail%2Fpgrouting-users%2F+swoodbridge+%22directions+problem%22&aq=f&aqi=&oq=&fp=2755c6b3e9b2e9
>
> Also look at the GSoC project at:
> http://sourceforge.net/apps/trac/opengraphrouter/wiki
>
> http://imaptools.com:8080/maps/gsoc2009/?zoom=4&lat=53.56076&lon=-122.60619&layers=BTTT&start=-131.686064%2058.79915&stop=-114.055306%2049.024077&method=GetDD&lang=eng
>
> This has an initial implementation is C++ but it is not well documented
> yet and needs a lot of additional work.
>
> -Steve
>
> Looks like the list moved so this might work better:
>
> http://www.google.com/#hl=en&newwindow=1&safe=off&&sa=X&ei=G2k8TeniA4Kr8Aapg5ylCg&ved=0CBIQBSgA&q=site%3Ahttp%3A//lists.postlbs.org/pipermail/pgrouting-users/+woodbridge+%22directions+problem%22&spell=1&fp=241062ed1d424d73
> _______________________________________________
> Pgrouting-users mailing list
> Pgrouting-users at lists.osgeo.org
> http://lists.osgeo.org/mailman/listinfo/pgrouting-users
>



-- 
Lorenzo Amato
lorenzo.amato at geosdi.org
lorenzotlc at gmail.com
lorenzo.amato at nsdi.it

Consiglio Nazionale delle Ricerche
Istituto di Metodologie per l'Analisi Ambientale - geoSDI
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.osgeo.org/pipermail/pgrouting-users/attachments/20110124/7e35d5d3/attachment-0001.html


More information about the Pgrouting-users mailing list