Table des matières
Dans ce chapitre, je vais tenter de mettre à profit les divers languages que j'ai peu testé sous Win32 avec PostGIS.
Les sources de PostGIS sont fournis avec un petit exemple de code en C que vous trouverez dans postgis-1.2.0\extra\wkb_reader. Nous allons ici adapter le code de certains fichiers à Windows pour pouvoir les utiliser pour un petit exemple que nous avons traiter dans le précédent chapitre.
Sous Win32 pour MinGW, on ne dispose pas du fichier d'en-tête endian.h, ouvrez donc le fichier wkbtest.h et au début du fichier remplacez la ligne
... #include <endian.h> ...
par
... #ifndef _ENDIAN_H_ #define _ENDIAN_H_ #include <sys/param.h> #endif /* _ENDIAN_H_ */ ...
Ouvrez ensuite le fichier readwkb.c et remplacer son contenu par le suivant -que j'ai ici adapté à notre exemple -
#include "wkbtest.h" /* example set-up Nous allons ici utiliser le premier exemple de données rencontrées au cours du chapitre précédent Ne pas oublier de définir convenablement les variables d'environnement PGDATABASE et PGUSER avant d'utiliser ce programme Also, change the "declare cursor" command so it returns the columns you want, and converts the geometry into wkb. */ void exit_nicely(PGconn *conn) { PQfinish(conn); exit(1); } void dump_bytes( char *a, int numb) { int t; for (t=0; t<numb; t++) { printf(" + Byte #%i has value %i (%x)\n",t,a[t],a[t]); } } // need to find the OID coresponding to the GEOMETRY type // // select OID from pg_type where typname = 'wkb'; int find_WKB_typeid(PGconn *conn) { PGresult *dbresult; char *num; if (PQstatus(conn) == CONNECTION_BAD) { fprintf(stderr, "no connection to db\n"); exit_nicely(conn); } dbresult = PQexec(conn, "select OID from pg_type where typname = 'bytea';"); if (PQresultStatus(dbresult) != PGRES_TUPLES_OK) { fprintf(stderr, "couldnt execute query to find oid of geometry type"); exit_nicely(conn); //command failed } if( PQntuples(dbresult) != 1) { fprintf(stderr, "query to find oid of geometry didnt return 1 row!"); exit_nicely(conn); } num = PQgetvalue(dbresult, 0, 0); // first row, first field PQclear(dbresult); return ( atoi(num) ); } main() { char *pghost, *pgport, *pgoptions, *pgtty; char *dbName; int nFields; int row, field; PGconn *conn; PGresult *res; int junk,j; char *field_name; int field_type; int WKB_OID; char conn_string[255]; bool *bool_val; int *int_val; float *float_val; double *double_val; char *char_val; char *wkb_val; char *table_name = "test"; char query_str[1000]; /* make a connection to the database */ conn = PQconnectdb(""); /* * check to see that the backend connection was successfully made */ if (PQstatus(conn) == CONNECTION_BAD) { fprintf(stderr, "%s", PQerrorMessage(conn)); exit_nicely(conn); } //what is the geometry type's OID #? WKB_OID = find_WKB_typeid(conn); /* start a transaction block */ res = PQexec(conn, "BEGIN"); if (!res || PQresultStatus(res) != PGRES_COMMAND_OK) { fprintf(stderr, "%s", PQerrorMessage(conn)); PQclear(res); exit_nicely(conn); } /* * should PQclear PGresult whenever it is no longer needed to avoid * memory leaks */ PQclear(res); /* * fetch rows from the pg_database, the system catalog of * databases */ sprintf(query_str, "DECLARE mycursor BINARY CURSOR FOR select text(genre), astext(geom) as astext,geometrytype(geom) as type_geometrique,srid(geom),assvg(geom) as svg,text(area2d(geom)) as aire from %s", table_name); printf(query_str); printf("\n"); res = PQexec(conn, query_str); if (!res || PQresultStatus(res) != PGRES_COMMAND_OK) { fprintf(stderr, "DECLARE CURSOR command failed\n"); fprintf(stderr, "%s", PQerrorMessage(conn)); PQclear(res); exit_nicely(conn); } PQclear(res); res = PQexec(conn, "FETCH ALL in mycursor"); if (!res || PQresultStatus(res) != PGRES_TUPLES_OK) { fprintf(stderr, "FETCH ALL command didn't return tuples properly\n"); fprintf(stderr, "%s", PQerrorMessage(conn)); PQclear(res); exit_nicely(conn); } j=0; for (row=0; row< PQntuples(res); row++) { j++; printf("------------------------------Ligne %i --------------------------\n",j); //not so efficient, but... for (field =0 ; field< PQnfields(res); field++) { field_type =PQftype(res,field); field_name = PQfname(res, field); //we just handle a few of the popular type since this is just an example if (field_type ==16)// bool { bool_val = (bool *) PQgetvalue(res, row, field); if (*bool_val) printf("%s: TRUE\n",field_name); else printf("%s: FALSE\n",field_name); } else if (field_type ==23 )//int4 (int) { int_val = (int *) PQgetvalue(res, row, field); printf("%s: %i\n",field_name,*int_val); } else if (field_type ==700 )//float4 (float) { float_val = (float *) PQgetvalue(res, row, field); printf("%s: %g\n",field_name,*float_val); } else if (field_type ==701 )//float8 (double) { double_val = (double *) PQgetvalue(res, row, field); printf("%s: %g\n",field_name,*double_val); } else if ( (field_type ==1043 ) || (field_type==25) )//varchar { char_val = (char *) PQgetvalue(res, row, field); printf("%s: %s\n",field_name,char_val); } else if (field_type == WKB_OID ) //wkb { char_val = (char *) PQgetvalue(res, row, field); printf("%s: ", field_name); // skip 4 bytes varlena size decode_wkb(char_val, &junk); printf("\n"); } } } PQclear(res); /* close the cursor */ res = PQexec(conn, "CLOSE mycursor"); PQclear(res); /* commit the transaction */ res = PQexec(conn, "COMMIT"); PQclear(res); /* close the connection to the database and cleanup */ PQfinish(conn); return 0; }
On va aussi adapter le Makefile de ce répertoire à notre exemple en remplaçant
CFLAGS=-I`pg_config --includedir` -L`pg_config --libdir` -lpq
par
CFLAGS=-I`pg_config --includedir` `pg_config --libdir`/libpq.dll
Depuis MinGW, il ne reste pluq qu'à préciser les variables de PostgreSQL nécessaire à l'utilisation du programme
export PGDATABASE=testgis export PGUSER=$USERNAME export PGHOST=localhost
où ici PGUSER correpond à votre utilisateur de MinGW. $USERNAME est une variable interne de MiNGW. Il ne reste plus qu'à faire[7]
make readwkb.exe
qui nous renverra
DECLARE mycursor BINARY CURSOR FOR select text(genre), astext(geom) as astext,geometrytype(geom) as type_geometrique,srid(geom),assvg(geom) as svg,text(area2d(geom)) as aire from test ------------------------------Ligne 1 -------------------------- text: pieton 1 astext: POINT(10 70) type_geometrique: POINT srid: -1 svg: cx="10" cy="-70" aire: 0 ------------------------------Ligne 2 -------------------------- text: pieton 2 astext: POINT(30 30) type_geometrique: POINT srid: -1 svg: cx="30" cy="-30" aire: 0 ------------------------------Ligne 3 -------------------------- text: batiment 1 astext: POLYGON((10 10,40 20,35 8,12 4,10 10)) type_geometrique: POLYGON srid: -1 svg: M 10 -10 40 -20 35 -8 12 -4 10 -10 aire: 228 ------------------------------Ligne 4 -------------------------- text: batiment 2 astext: POLYGON((10 40,20 30,30 40,40 35,50 60,35 80,20 60,10 40)) type_geometrique: POLYGON srid: -1 svg: M 10 -40 20 -30 30 -40 40 -35 50 -60 35 -80 20 -60 10 -40 aire: 1050 ------------------------------Ligne 5 -------------------------- text: batiment 3 astext: POLYGON((10 95,20 95,20 135,10 135,10 95)) type_geometrique: POLYGON srid: -1 svg: M 10 -95 20 -95 20 -135 10 -135 10 -95 aire: 400 ------------------------------Ligne 6 -------------------------- text: pieton 3 astext: POINT(35 70) type_geometrique: POINT srid: -1 svg: cx="35" cy="-70" aire: 0 ------------------------------Ligne 7 -------------------------- text: pieton 4 astext: POINT(35 60) type_geometrique: POINT srid: -1 svg: cx="35" cy="-60" aire: 0 ------------------------------Ligne 8 -------------------------- text: bordure 1 route astext: LINESTRING(1 85,50 85) type_geometrique: LINESTRING srid: -1 svg: M 1 -85 50 -85 aire: 0 ------------------------------Ligne 9 -------------------------- text: bordure 2 route astext: LINESTRING(1 92,50 92) type_geometrique: LINESTRING srid: -1 svg: M 1 -92 50 -92 aire: 0
Pour de plus amples informations sur l'interface client libpq en C de PostgreSQL, merci de vous reportez à la documentation de PostgreSQL. Consultez notamment
http://traduc.postgresqlfr.org/pgsql-8.2.1-fr/client-interfaces.html
Ici mon exemple est basé sur celui du chapitre 4 "Exemples de requêtes spatiales II"
On va commencer par définir la page index.php qui va contenir une liste déroulante de quelques requêtes spatiales qui seront afficher depuis une liste déroulante
<html> <body bgcolor='#AAEEFC'> <!-- Formulaire générale --> <form method='get' action='<?php $PHP_SELF;?>'> <!-- Insertion des requetes possibles --> <select name='id'> <option value='0'>Affichage normale</option> <option value='1'>Afficher les personnes contenus dans le bâtiment 'Résidence des Mousquetaires'</option> <option value='2'>Afficher les bâtiments qui ne contiennent aucune personne</option> <option value='3'>Afficher les bâtiments contenus dans les parcs</option> <option value='4'>Afficher les points d'intersection entre les petites et les grandes routes</option> <option value='5'>Afficher l'intersection entre la rivière et les parcs</option> <option value='6'>Afficher les personnes présentes autour de la rivière dans une rayon de 5 mètres</option> </select> <input type='submit' value='recharger'> <br> <?php /* On commence par récupérer la valeur de id passer au script */ $id = (( isset($_GET["id"]) )? $_GET["id"]: "0"); ?> <!-- insertion du svg --> <embed name='SVG' type='image/svg+xml' src='madatabase.php?id=<?php echo $id;?>' width='600px' height='509.505693569px' /> <!-- Fin du formulaire --> </form> </body> </html>
Lors de la soumission du formulaire ce script qui s'appelle lui-même fait appel au script madatabase.php suivant:
<?php header("Content-type: image/svg+xml"); echo '<?xml version="1.0" encoding="iso-8859-1"?>'; $id = (( isset($_GET["id"]) )? $_GET["id"]: "0"); if ( extension_loaded('pgsql') != 1) { switch (PHP_OS) { case "WINNT": if (!extension_loaded('pgsql')) dl("php_pgsql.dll"); break; default: if (!extension_loaded('pgsql')) dl("php_pgsql.so"); break; } } ?> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd"> <?php $pg_hote = "localhost"; $pg_base_de_donnees = "madatabase"; $pg_utilisateur = "postgres"; $pg_mot_de_passe = " "; $pg_srid="-1"; $db_handle = pg_connect("host=".$pg_hote." dbname=".$pg_base_de_donnees." user=".$pg_utilisateur." password=".$pg_mot_de_passe.""); $widthsvg = 600; $Requete_MS_Tables = "SELECT * from mapserver_desc"; $Resultat_MS_Tables = pg_exec( $db_handle, $Requete_MS_Tables ); // tableaux nécessaire pour calculer l'extent $Xmin = array(); $Xmax = array(); $Ymin = array(); $Ymax = array(); while( $MS_Ligne = pg_fetch_object( $Resultat_MS_Tables) ) { /* calcul de l'Extent pour chaque table */ $colonne_geometrique = "the_geom"; $Requete_Extent = "select xmin(extent(".$colonne_geometrique.")), ymin(extent(".$colonne_geometrique.")), xmax(extent(".$colonne_geometrique.")), ymax(extent(".$colonne_geometrique.")) from ".$MS_Ligne->ms_table; $Resultat_Extent = pg_exec( $db_handle, $Requete_Extent ); while ( $Row_Extent = pg_fetch_object( $Resultat_Extent )) { $Xmin[] = $Row_Extent->xmin; $Ymin[] = $Row_Extent->ymin; $Xmax[] = $Row_Extent->xmax; $Ymax[] = $Row_Extent->ymax; } } /* Calcul de l'Extent générale et des dimensions de l'image */ $xmin = min($Xmin); $xmax = max( $Xmax ); $ymin = min($Ymin ); $ymax = max ($Ymax); $width= abs($xmax-$xmin); $height= abs($ymax-$ymin); $rapport= $width / $height; echo "<svg id='racine' width='".$widthsvg."px' height='".$widthsvg/$rapport."px' viewBox='".$xmin." ".-1*$ymax." ".$width." ".$height."'>"; echo " <style type='text/css'><![CDATA[ .StyleFondsmallroadsOn { fill:none; stroke:rgb(80,80,80); stroke-width:2} .StyleFondsmallroadsOff { fill:none; stroke:rgb(80,80,80); stroke-width:2} .StyleFondgreatroadsOn { fill:none; stroke:rgb(125,125,125); stroke-width:3} .StyleFondgreatroadsOff { fill:none; stroke:rgb(80,80,80); stroke-width:3} .StyleFondparcsOn { fill:rgb(0,123,0); stroke:rgb(255,255,255); stroke-width:0.1} .StyleFondparcsOff { fill:none; stroke:rgb(255,255,255); stroke-width:0.1} .StyleFondriversOn { fill:rgb(0,12,189); stroke:rgb(0,12,189); stroke-width:1} .StyleFondriversOff { fill:none; stroke:rgb(80,80,80); stroke-width:11} .StyleFondbuildingsOn { fill:rgb(234,156,78); stroke:black; stroke-width:0.4 } .StyleFondbuildingsOff { fill:rgb(0,30,0); stroke:black; stroke-width:0.4 } .StyleFondpersonnesOn { fill:rgb(255,0,0); stroke:black; stroke-width:0.1 } .StyleFondpersonnesOff { fill:rgb(255,0,0); stroke:black; stroke-width:0.1 } ]]> </style>"; /* On liste les tables à afficher ici */ $Table_Geom = array(); $Table_Geom[] = "small_roads"; $Table_Geom[] = "great_roads"; $Table_Geom[] = "parcs"; $Table_Geom[] = "rivers"; $Table_Geom[] = "buildings"; $Table_Geom[] = "personnes"; /* On ferme le fichier SVG */ for($Iter_Geom = 0;$Iter_Geom< count($Table_Geom);$Iter_Geom++) {// Début de la boucle sur les tables $table = $Table_Geom[$Iter_Geom]; $Requete_SVG = "select AsSVG(the_geom,0) from ".$table; $Requete_Geom_Type = "select type from geometry_columns where f_table_name like '".$table."'"; $Resultat_SVG = pg_exec($db_handle, $Requete_SVG); $Resultat_Geom_Type = pg_result(pg_exec($db_handle, $Requete_Geom_Type), 0, 0); $Nb_ligne_table_SVG = pg_numrows( $Resultat_SVG ); for($It_SVG=0;$It_SVG< $Nb_ligne_table_SVG;$It_SVG++) { echo "<g id='id_".$table."_".$It_SVG."' class='StyleFond".str_replace("_","",$table)."On'>"; switch ($Resultat_Geom_Type) { case "POINT": echo "<circle ".pg_result($Resultat_SVG,$It_SVG,0)." r='1'/>"; break; default: echo "<path id='id_path_".$table."_".$It_SVG."' d='".pg_result($Resultat_SVG,$It_SVG,0)."'/>"; break; } echo "</g>"; } }// Fin de la boucle sur les tables /* $Table_Geom = array(); $Table_Geom[] = "small_roads"; $Table_Geom[] = "great_roads"; $Table_Geom[] = "parcs"; $Table_Geom[] = "rivers"; $Table_Geom[] = "buildings"; $Table_Geom[] = "personnes"; for($Iter_Geom = 0;$Iter_Geom< count($Table_Geom);$Iter_Geom++) {// Début de la boucle sur les tables $table = $Table_Geom[$Iter_Geom]; $Requete_Gid = "select gid from ".$table; $Resultat_Gid = pg_exec($db_handle, $Requete_Gid); $Nb_ligne_table_Gid = pg_numrows( $Resultat_Gid ); for($It_SVG=0;$It_SVG< $Nb_ligne_table_Gid;$It_SVG++) { if (($table=="great_roads")||($table=="small_roads")) { $Requete_Affichage_buildings = "select assvg(translate(pointonsurface(the_geom),-12,0),1),data from ".$table." where gid=".intval($It_SVG+1); $Resultat_Affichage_buildings = pg_exec($db_handle, $Requete_Affichage_buildings); $my_SVG->ecrire("<text ".pg_result($Resultat_Affichage_buildings,0,0)." fill='black' font-size='3' font-color='black' >".pg_result($Resultat_Affichage_buildings,0,1)." </text>"); } } } */ /* Traitement des cas */ switch ($id) { case 1: $Requete_Affichage = "select AsSvg(personnes.the_geom,0), AsSvg(PointOnSurface(personnes.the_geom),1), personnes.data from personnes,buildings where within(personnes.the_geom,buildings.the_geom) and buildings.data like 'Résidence des Mousquetaires'"; $Resultat_Affichage = pg_exec($db_handle, $Requete_Affichage); for($It_SVG=0;$It_SVG< pg_numrows($Resultat_Affichage);$It_SVG++) { echo "<circle fill='blue' ".pg_result($Resultat_Affichage,$It_SVG,0)." r='2'> <animate attributeName='fill' values='blue;lightskyblue' dur='1.5s' repeatCount='indefinite' begin='0s' calcMode='discrete' /> </circle>"; echo "<text ".pg_result($Resultat_Affichage,$It_SVG,1)." fill='black' font-size='3' font-color='white' >".pg_result($Resultat_Affichage,$It_SVG,2)." </text>"; } break; case 2: $Requete_Affichage = "select AsSvg(the_geom,0), AsSvg(PointOnSurface(the_geom),1), b.data from buildings b, (select geomunion(the_geom) from personnes) gp where not intersects(gp.geomunion,b.the_geom)"; $Resultat_Affichage = pg_exec($db_handle, $Requete_Affichage); for($It_SVG=0;$It_SVG< pg_numrows($Resultat_Affichage);$It_SVG++) { echo "<path fill='rgb(234,156,78)' d='".pg_result($Resultat_Affichage,$It_SVG,0)."'> <animate attributeName='stroke' values='white;lightskyblue' dur='1.5s' repeatCount='indefinite' begin='0s' calcMode='discrete'/> </path>"; echo "<text ".pg_result($Resultat_Affichage,$It_SVG,1)." fill='black' font-size='3' font-color='white' >".pg_result($Resultat_Affichage,$It_SVG,2)." </text>"; } break; case 3: $Requete_Affichage = "select AsSvg(the_geom,0), AsSvg(PointOnSurface(the_geom),1), b.data from buildings b where Contains(parcs.the_geom,b.the_geom)"; $Resultat_Affichage = pg_exec($db_handle, $Requete_Affichage); for($It_SVG=0;$It_SVG< pg_numrows($Resultat_Affichage);$It_SVG++) { echo "<path fill='rgb(234,156,78)' d='".pg_result($Resultat_Affichage,$It_SVG,0)."'> <animate attributeName='stroke' values='white;lightskyblue' dur='1.5s' repeatCount='indefinite' begin='0s' calcMode='discrete'/> </path>"; echo "<text ".pg_result($Resultat_Affichage,$It_SVG,1)." fill='black' font-size='3' font-color='white' >".pg_result($Resultat_Affichage,$It_SVG,2)." </text>"; } break; case 4: $Requete_Affichage = "select assvg(intersection(g.the_geom,s.the_geom),1), astext(intersection(g.the_geom,s.the_geom)), assvg(intersection(g.the_geom,s.the_geom),0) from great_roads g, small_roads s where intersects(g.the_geom,s.the_geom)"; $Resultat_Affichage = pg_exec($db_handle, $Requete_Affichage); for($It_SVG=0;$It_SVG< pg_numrows($Resultat_Affichage);$It_SVG++) { echo "<circle fill='blue' ".pg_result($Resultat_Affichage,$It_SVG,2)." r='2'> <animate attributeName='fill' values='blue;lightskyblue' dur='1.5s' repeatCount='indefinite' begin='0s' calcMode='discrete' /> </circle>"; echo "<text ".pg_result($Resultat_Affichage,$It_SVG,0)." fill='black' font-size='3' font-color='black' >".pg_result($Resultat_Affichage,$It_SVG,1)." </text>"; } break; case 5: $Requete_Affichage = "select AsSvg(Intersection(r.the_geom,p.the_geom),0) from rivers r, parcs p where intersects(r.the_geom,p.the_geom)"; $Resultat_Affichage = pg_exec($db_handle, $Requete_Affichage); for($It_SVG=0;$It_SVG< pg_numrows($Resultat_Affichage);$It_SVG++) { echo "<path d='".pg_result($Resultat_Affichage,$It_SVG,0)."'> <animate attributeName='fill' values='blue;lightskyblue' dur='1.5s' repeatCount='indefinite' begin='0s' calcMode='discrete'/> </path>"; } break; case 6: $Requete_Affichage = "select AsSvg(p.the_geom,0), AsSvg(PointOnSurface(p.the_geom),1), p.data from personnes p where contains(buffer(rivers.the_geom,5),p.the_geom)"; $Resultat_Affichage = pg_exec($db_handle, $Requete_Affichage); for($It_SVG=0;$It_SVG< pg_numrows($Resultat_Affichage);$It_SVG++) { echo "<circle fill='blue' ".pg_result($Resultat_Affichage,$It_SVG,0)." r='2'> <animate attributeName='fill' values='blue;lightskyblue' dur='1.5s' repeatCount='indefinite' begin='0s' calcMode='discrete' /> </circle>"; echo "<text ".pg_result($Resultat_Affichage,$It_SVG,1)." fill='black' font-size='3' font-color='white' >".pg_result($Resultat_Affichage,$It_SVG,2)." </text>"; } $Requete_Affichage_Buffer = "select AsSvg(buffer(the_geom,5),0) from rivers"; $Resultat_Affichage_Buffer = pg_exec($db_handle, $Requete_Affichage_Buffer); for($It_SVG=0;$It_SVG< pg_numrows($Resultat_Affichage_Buffer);$It_SVG++) { echo "<path fill='none' d='".pg_result($Resultat_Affichage_Buffer,$It_SVG,0)."'> <animate attributeName='stroke' values='red;lightskyred' dur='1.5s' repeatCount='indefinite' begin='0s' calcMode='discrete'/> </path>"; } break; default: break; }// Fin des test pour id /* Connexion à PostgreSQL => Fermeture */ pg_close($db_handle); ?> </svg>
On aura par exemple l'image suivante
[7] On se sera assurer ici que l'utilisateur sous MinGW soit un super-utilisateur dans PostgreSQL. Pour celà, consultez le chapitre 3: "Devenir soi-même super-utilisateur de PostgreSQL (pour l'utilisateur de MinGW)"