PostGIS et son utilisation avec un framework moderne

Publié le Mis à jour le Par

Pour rappel, PostGIS est une extension ajoutant des fonctionnalités géospatiales au systeme de gestion de base de données PostgreSQL, en se basant sur la spécification OpenGIS. Si votre application manipule des repères ou zones géographiques, des objets géométriques ou des distances, c’est donc un outil à ne pas négliger.


Malgré ses 10 ans d’existence (dont 6 en version stable), PostGIS reste très peu utilisé dans les projets Web, souvent à cause de son intégration compliquée au sein des différents frameworks. Nous allons voir ici que ce n’est pourtant pas toujours le cas, en décrivant son installation et son utilisation au sein d’un projet Play! Framework

Installation & Configuration

Les consignes ci-dessous se basent sur la distribution Ubuntu 10.04 et sur la version 8.4 de PostgreSQL, mais l’installation varie assez peu pour les autres distributions et/ou versions, des paquets étant disponibles soit nativement, soit sur internet (voir http://trac.osgeo.org/postgis/wiki/UsersWikiMain#CompilationandInstallationGuides pour plus d’informations).

L’installation se fait grâce à deux paquets :

sudo apt-get install postgis

sudo apt-get install postgresql-8.4-postgis

Une fois ces deux paquets téléchargés, l’installation de PostGIS sur votre machine est terminée. Néanmoins, vous ne pouvez pas encore utiliser ses fonctionnalités avec votre base de données : pour cela, quelques manipulations supplémentaires sont encore nécessaires :

1.Connexion en tant qu’administrateur de serveur PostgreSQL

sudo su postgres

2. Activation du langage PL/SQL sur votre base de données :

createlang plpgsql my_geo_database

3. Ajout des fonctions PostGIS dans la base de données :

psql -d my_geo_database -f /usr/share/postgresql/8.4/contrib/postgis-1.5/postgis.sql

psql -d my_geo_database -f /usr/share/postgresql/8.4/contrib/postgis-1.5/spatial_ref_sys.sql

4. Changer le propriétaire des tables créées (et ainsi les rendre accessibles depuis vos applications)

psql my_geo_database -c "ALTER TABLE geometry_columns OWNER TO my_user"

psql my_geo_database -c "ALTER TABLE spatial_ref_sys OWNER TO my_user"

Ça y est, votre base de donnée est prête ! Voyons maintenant comment l’utiliser dans un projet Play!

Utilisation dans Play! Framework

Imaginons une application Web répertoriant les célèbres cabines téléphoniques anglaises(lien), et fournissant un certain nombre de services au visiteur, notamment basés sur la géolocalisation.

Une fois le projet Play initialisé, la première étape est de télécharger les quelques librairies (JAR) nécessaires à l’utilisation de PostGIS:

qui fournissent le dialecte JPA permettant d’utiliser PostGIS conjointement à Hibernate.

Une fois les JAR ajoutés au répertoire lib/, réfléchissons à la conception du modèle de l’application. Commençons simplement en créant une classe PhoneBox, avec comme attributs un label, une latitude et une longitude :


public class PhoneBox extends Model public String label;
public double latitude;
public double longitude

public PhoneBox(String label, double latitude, double longitude) {
this.label = label;
this.latitude = latitude;
this.longitude = longitude;

}

Une cabine téléphonique est un élément assez petit (sur une carte du moins) pour être considéré comme un point (par opposition aux polygones). Ca tombe bien, PostGIS utilise justement cette notion de Point :


import org.postgis.Point;

public class PhoneBox extends Model public String label;
public Point location;

public PhoneBox(String label, double latitude, double longitude) {
this.label = label;
this.location = new Point(longitude, latitude);

}

Nous avons donc remplacé le couple latitude / longitude par un seul attribut de la classe Point et modifié le constructeur afin d’instancier cet attribut à partir de ces mêmes latitudes et longitudes.

Comme beaucoup d’autres frameworks Java, Play! se base sur Hibernate pour gérer la persistence des objets en base de données. Malheureusement, Hibernate ne reconnait pas nativement les types utilisés par PostGIS : C’est là que va intervenir la bibliothèque Hibernate Spatial, installée précédemment.


import org.postgis.Point;

@Entity
@Table(name = « phonebox »)
public class PhoneBox extends Model @Column(name = « label », length = 100, nullable = false)
public String label;

@Column(name = « position », columnDefinition = « GEOMETRY »)
@Type(type = « org.hibernatespatial.GeometryUserType »)
public Point location;

// pas de changement au niveau du constructeur

En plus des annotations usuelles, l’attribut columnDefinition de l’annotation @Column détermine le type du champ en base de données, et l’annotation @Type détermine quand à elle la classe à laquelle Hibernate doit se référer pour gérer la persistance de l’attribut, ainsi que les requêtes HQL le concernant.

Après avoir relancé l’application, on peut voir que la table phonebox a bien été créée dans notre base de données, avec les champs correspondants.

Ajoutons rapidement quelques cabines téléphoniques pour voir leur représentation dans la base de données :

Les objets sont donc correctement enregistrés avec leurs données. On peut remarquer que le champ location est représenté par une chaîne de caractère peu lisible, c’est le cas avec PostGIS pour tous les champs de type geometry.

Une fois nos cabines téléphoniques disponibles, voyons ce que PostGIS nous offre comme possibilités d’utilisations. Une fonctionnalité pourrait par exemple consister à récupérer les n cabines téléphoniques les plus proches d’une position donnée. Pour cela, nous allons utiliser une requête SQL utilisant les fonctions PL/SQL ajoutées plus tôt :


public static List closest(double latitude, double longitude, int nb)
TypedQuery q = JPA.em().createQuery("SELECT p FROM PhoneBox p ORDER BY ST_DISTANCE(ST_MAKEPOINT(:longitude:latitude,), position)", PhoneBox.class);

List list = q
.setParameter(« latitude », latitude)
.setParameter(« longitude », longitude)
.setMaxResults(nb)
.getResultList();

return list;

  • La fonction ST_MAKEPOINT sert, comme son nom l’indique, à créer un point à partir des latitude et longitude fournies en arguments ;
  • la fonction ST_DISTANCE, quant à elle, récupère la distance entre deux objets géométriques (quels qu’ils soient).

En ordonnant les résultats de la requête en fonction de cette distance, on obtient bien les cabines attendues. En les affichant sur une carte Google Maps (les latitudes et longitudes sont directement récupérées depuis l’objet Point):

Mais PostGIS ne se limite pas seulement au calcul de distances ! Le nombre de fonctions à disposition est assez impressionnant, et couvre énormément de cas d’usages. Pour s’en rendre compte, il suffit de regarder quelques projets où PostGIS a été mis à l’ouvrage :

  • La ville de Boston utilise PostGIS pour stocker ses données « ouvertes » et les rendre disponibles à la consultation :
  • L’IGN, stocke également une partie de son immense base de données sur PostgreSQL/PostGIS, avec des interfaces Web et natives à destination de ses chercheurs :

Mais attention, malgré toutes ses qualités, PostGIS n’est pas bon à utiliser dans tous vos projets géospatiaux : dans la plupart des cas (et souvent pour les plus basiques), passer par une API tierce (Google, Bing, OSM…) apportera une simplicité et un découplage bienvenus.

Futures perspectives

Actuellement, la version stable de PostGIS est la 1.5. La version 2 est sur les rails, et apportera son lot de nouveautés, notamment au niveau de la topologie et du support de la 3D, ainsi qu’un nettoyage de rigueur des fonctions dépréciées.

Pour plus d’informations sur cette seconde version, je vous renvoie vers les présentations suivantes ayant eu lieu lors lors de la dernière session PostgreSQL en Juin dernier :