Libpgeasy est une C-API pour PostgreSQL bien sympathique. Je vais l'utiliser ici dans le cadre de mon précédent billet. En fait pour tout dire, ce billet vient donc à la suite du précédent. Ce sera pour moi aussi le moment de faire quelques révisions en C. Mon code fourni ici est pas nécessairement optimisé! Alors reprenons le fil de nos investigations.
N.B: coder la dernière partie de mon précédent billet en PHP est facilement réalisable. C'est ici pour moi l'occasion surtout d'utiliser une C-API de PostgreSQL assez basique pour mon apprentissage personnel.
Nous avons pour le moment la requête suivante
routing=# SELECT * from tsp('select distinct source as source_id, x1::double precision as x, y1::double precision as y from
troncon_route_edges where source in (43,30,18,41,1,13)','43,30,18,41,1,13',41);
vertex_id | edge_id | cost
-----------+-----------+-----------------------
41 | 138321080 | 0
30 | 0 | 2.12199579047121e-314
1 | 8 | 6.84394238386657e-316
18 | 16 | 2.12199611507234e-314
13 | 138523056 | 6.79720117142741e-313
43 | 406 | 1.23516411460312e-322
(6 lignes)
Mon but ici est de récupérer les couples (41,30), (30,1),(1,18) que j'aimerais appliquer à la suite à la fonctionnalité
shortest_path_astar() de pgrouting. Autrement dit si j'ai au préalable une table
aller
CREATE TABLE aller(gid int4) WITH oids;
SELECT AddGeometryColumn( 'aller', 'the_geom', -1, 'MULTILINESTRING', 2 );
dans laquelle je souhaiterais stocker les diverses portions de chemins renvoyés par les couples (source,target) - (41,30),(30,1) - pour
shortest_path_astar() en faisant
INSERT INTO aller(the_geom)
(
SELECT the_geom FROM troncon_route_edges WHERE edge_id IN
(SELECT edge_id FROM shortest_path_astar('SELECT id,source,target,cost,
reverse_cost, x1,y1,x2,y2 FROM troncon_route_edges',????,???,false,true)
)
);
où "????,???" vaudra "41,30" puis "30,1" etc...
Installation de libpgeasy
Rien de compliqué pour l'installation. Chez moi, PostgreSQL est installé Ã
/opt/pgsql et c'est la version 8.2.3 que j'utilise. Conformément à la documentation, on fera tout simplement
wget ftp://gborg.postgresql.org/pub/pgeasy/stable/libpgeasy-3.0.4.tar.gz
tar xvzf libpgeasy-3.0.4.tar.gz
cd libpgeasy-3.0.4
export PATH=/opt/pgsql/bin:$PATH
export CPPFLAGS=-I$(pg_config --includedir)
export LDFLAGS=-L$(pg_config --libdir)
./configure --prefix=/opt/pgsql
make
make install
ldconfig
Le programme calcul_trajet.c
Mon code est le suivant
/* calcul_trajet.c */
#include <stdlib.h>
#include <stdio.h>
#include <libpq-fe.h>
#include <libpgeasy.h>
int main(int argc, char * argv[])
{
char vertex_id[2];
int VertexIndice=0; int Nb_vertex=0,i;
char vertex[53][2];
for(i=0;i<=2;i++)
{ for(VertexIndice=0;VertexIndice<=53;VertexIndice++)
{
vertex[VertexIndice][i]=' ';//printf("%d\n",vertex[VertexIndice]);
}
}
char query[600]="SELECT vertex_id::int from tsp('select distinct source as source_id\
, x1::double precision as x, y1::double precision as y from \
troncon_route_edges where source in (";
char query2[800]="INSERT INTO aller(the_geom) SELECT the_geom FROM troncon_route_edges\
WHERE edge_id IN (SELECT edge_id FROM shortest_path_astar('SELECT id,source,target,cost,reverse_cost,\
x1,y1,x2,y2 FROM troncon_route_edges',";
PGconn * connexion;
if (argc != 4)
halt("Usage: %s \"dbname=XXX user=XXX host=XXX\" \"liste de points\" startpoint\n\
Exemple %s \"dbname=routing user=postgres\" \"43,30,18,41,1\" 30\n", argv[0],argv[0]);
connexion = connectdb( argv[1] ? argv[1]:"");
on_error_stop();
strcat(query,argv[2]);
strcat(query,")','");
strcat(query,argv[2]);
strcat(query,"',");
strcat(query,argv[3]);
strcat(query,")");
doquery(query);
printf("-- REQUETE %s",query);VertexIndice=0;
while( fetch(vertex_id)!=END_OF_TUPLES)
{
printf("-- noeud = %c%c%c\n",vertex_id[0],vertex_id[1],vertex_id[2]);
vertex[VertexIndice][0]=vertex_id[0];
vertex[VertexIndice][1]=vertex_id[1];
vertex[VertexIndice][2]=vertex_id[2];
VertexIndice++;Nb_vertex++;
}
for(VertexIndice=1;VertexIndice<Nb_vertex;VertexIndice++)
{
; char a[2]={vertex[VertexIndice][0],vertex[VertexIndice][1]};
char b[2]={vertex[VertexIndice-1][0],vertex[VertexIndice-1][1]};
char c[10]={'0','1','2','3','4','5','6','7','8','9'};
int test_a=0,test_b=0;
for(i=0;i<10;i++)
{if (c[i]==a[1]) test_a=1;if (c[i]==b[1]) test_b=1;}
if ((test_a==1)&&(test_b==1))
printf("%s %c%c,%c%c,false,true));\n",query2,b[0],b[1],a[0],a[1]);
if ((test_a==0)&&(test_b==0))
printf("%s %c,%c,false,true));\n",query2,b[0],a[0]);
if ((test_a==1)&&(test_b==0))
printf("%s %c,%c%c,false,true));\n",query2,b[0],a[0],a[1]);
if ((test_a==0)&&(test_b==1))
printf("%s %c%c,%c,false,true));\n",query2,b[0],b[1],a[0]);
}
disconnectdb();
return 0;
}
Un petit Makefile pour la route
On pourra par exemple utiliser le
Makefile suivant
POSTGRES_HOME=/opt/pgsql
CFLAGS= -O
TARGET = calcul_trajet
all : $(TARGET)
%: %.c
gcc $(CFLAGS) -I$(POSTGRES_HOME)/include -o $@ $@.c -L$(POSTGRES_HOME)/lib -lpgeasy
clean:
rm -f $(TARGET)
Pour compiler le programme, on fera tout simplement
make
Utilisation
Un simple
./calcul_trajet me donne un descriptif succinct de son utilisation
root@bremko:/mnt/sources/tmp# ./calcul_trajet
Usage: ./calcul_trajet "dbname=XXX user=XXX host=XXX" "liste de points" startpoint
Exemple ./calcul_trajet "dbname=routing user=postgres" "43,30,18,41,1" 30
Je n'aurais donc qu'Ã faire pour peupler ma table
aller
./calcul_trajet "dbname=routing user=postgres host=localhost" "43,30,18,41,1,13" 41 | psql -U postgres -d routing
Si on enlève la commande
" | psql -U postgres -d routing", on a alors la sortie suivante dans le terminal
./calcul_trajet "dbname=routing user=postgres host=localhost" "43,30,18,41,1,13" 41
-- REQUETE SELECT vertex_id::int from tsp('select distinct source as source_id,
x1::double precision as x, y1::double precision as y from troncon_route_edges
where source in (43,30,18,41,1,13)','43,30,18,41,1,13',41)
-- noeud = 41
-- noeud = 30
-- noeud = 1
-- noeud = 18
-- noeud = 13
-- noeud = 43
INSERT INTO aller(the_geom) SELECT the_geom FROM troncon_route_edges WHERE edge_id
IN (SELECT edge_id FROM shortest_path_astar('SELECT id,source,target,cost,reverse_cost,
x1,y1,x2,y2 FROM troncon_route_edges', 41,30,false,true));
INSERT INTO aller(the_geom) SELECT the_geom FROM troncon_route_edges WHERE edge_id
IN (SELECT edge_id FROM shortest_path_astar('SELECT id,source,target,cost,reverse_cost,
x1,y1,x2,y2 FROM troncon_route_edges', 30,1,false,true));
INSERT INTO aller(the_geom) SELECT the_geom FROM troncon_route_edges WHERE edge_id
IN (SELECT edge_id FROM shortest_path_astar('SELECT id,source,target,cost,reverse_cost,
x1,y1,x2,y2 FROM troncon_route_edges', 1,18,false,true));
INSERT INTO aller(the_geom) SELECT the_geom FROM troncon_route_edges WHERE edge_id
IN (SELECT edge_id FROM shortest_path_astar('SELECT id,source,target,cost,reverse_cost,
x1,y1,x2,y2 FROM troncon_route_edges', 18,13,false,true));
INSERT INTO aller(the_geom) SELECT the_geom FROM troncon_route_edges WHERE edge_id
IN (SELECT edge_id FROM shortest_path_astar('SELECT id,source,target,cost,reverse_cost,
x1,y1,x2,y2 FROM troncon_route_edges', 13,43,false,true));
Ce qui se traduira donc - comme déjà attendu dans le bille précédent - par le schéma suivant pour la table
aller

Parcours à effectuer dans l'ordre 41,30,1,18,13,43 en partant du noeud 41.