Développer une application avec pgrouting sous Windows (7):OpenLayers 3

Nous arrivons à la fin de cette série. Après avoir mis en place la base de données Postgres/Postgis, nous avons configuré Geoserver pour servir les résultats de pgrouting en un lux WMS. Il ne reste plus qu’à  créer une page html avec openlayers pour pouvoir entrer les points de départ et d’arrivée et de visualiser l’itinéraire calculé.

Bien sûr, il y a moyen de réaliser une application complexe. Mais ici nous resterons sur quelque chose d’assez modeste. Nous allons afficher un fond de carte OpenStreetMap, et permettre à l’utilisateur de cliquer sur cette carte pour déterminer le point de départ de l’itinéraire. Quand il cliquera sur un deuxième point de la carte, nous considérerons que c’est le point d’arrivée souhaité. La page enverra alors la requête au flux WMS de Geoserver pour récupérer une entité vecteur représentant l’itinéraire calculé par pgrouting  et nous l’afficherons sur la carte.

Pour compléter la page nous mettrons un bouton pour réinitialiser la carte, une ligne de message pour tenir au curant l’utilisateur de l’action en cours, et une image pour indiquer l’état du transfert entre le serveur et l’application.

Texte de la page html

<!DOCTYPE html>
<html>
<head>
<title>Recherche d’itinéraire avec pgRouting </title>
<meta charset= »utf-8″>
<style>
#map {
width: 100%;
height: 800px;}
</style>
</head>
<body>
<div id= »map »></div>
<div id= »desc »>Cliquez sur la carte pour définir le point de départ – cliquez à nouveau pour définir le point d’arrivée – la route sera tracée</div><div align= »center »><img src= »0.png » id= »image » width= »60″ height= »60″></div>
<button id= »clear »>Effacer</button>
<script src= »http://openlayers.org/en/v3.16.0/build/ol.js »></script>
<script type= »text/javascript »>
var map = new ol.Map({
target: ‘map’,
layers: [
new ol.layer.Tile({
source: new ol.source.OSM()
})
],
view: new ol.View({
center: [-500000, 6172000],
zoom: 12
}),
controls: ol.control.defaults({
attributionOptions: {
collapsible: false
}
})
});

// Les points de départ et d’arrivée.
var startPoint = new ol.Feature();
var destPoint = new ol.Feature();

// La couche vecteur utilisée pour afficher les points de départ et d’arrivée.
var vectorLayer = new ol.layer.Vector({
source: new ol.source.Vector({
features: [startPoint, destPoint]})
});

map.addLayer(vectorLayer);
var params = {
LAYERS: ‘pgrouting:itineraire’,
FORMAT: ‘image/png’
};
// La fonction de transformation des coordonnées à partir de l’EPSG:3857
// vers  EPSG:4326.
var transform = ol.proj.getTransform(‘EPSG:3857’, ‘EPSG:4326’);

// Mise en place d’une écoute sur les évenements de la carte.
map.on(‘click’, function(event) {
if (startPoint.getGeometry() == null) {
// Premier click.
startPoint.setGeometry(new ol.geom.Point(event.coordinate));
} else if (destPoint.getGeometry() == null) {
// Deuxième click.
destPoint.setGeometry(new ol.geom.Point(event.coordinate));
// Transformations des coordonnées à partir de celle de la carte (EPSG:3857)
// vers celle du serveur (EPSG:4326).
var startCoord = transform(startPoint.getGeometry().getCoordinates());
var destCoord = transform(destPoint.getGeometry().getCoordinates());
var viewparams = [
‘x1:’ + startCoord[0], ‘y1:’ + startCoord[1],
‘x2:’ + destCoord[0], ‘y2:’ + destCoord[1] ];
params.viewparams = viewparams.join(‘;’);
var source =new ol.source.ImageWMS({
url: ‘http://localhost:8080/geoserver/pgrouting/wms’,
params: params
})
result = new ol.layer.Image({
source: source
});
source.on(‘imageloadstart’, function() {
document.getElementById(« desc »).innerHTML = »chargement… »;
document.getElementById(« image »).src = »tree_loading.gif »;
});
source.on(‘imageloadend’, function() {
document.getElementById(« desc »).innerHTML = »terminé OK! »;
document.getElementById(« image »).src = »OK.jpg »;
});
source.on(‘imageloaderror’, function() {
document.getElementById(« desc »).innerHTML = »erreur! »;
document.getElementById(« image »).src = »no.png »;
});
map.addLayer(result);
}
});

var clearButton = document.getElementById(‘clear’);
clearButton.addEventListener(‘click’, function(event) {
// Réinitialise les entités  « start » et « destination » .
startPoint.setGeometry(null);
destPoint.setGeometry(null);
document.getElementById(« desc »).innerHTML = »Cliquez sur la carte pour définir le point de départ – cliquez à nouveau pour définir le point d’arrivée – la route sera tracée »;
document.getElementById(« image »).src = »0.png »;
// Remove the result layer.
map.removeLayer(result);
});
</script>
</body>
</html>

 

Voyons maintenant en détail les différentes parties du code.

Pour ce qui est du début, que du classique:

<style>
#map {
width: 100%;
height: 800px;}

Nous définissons la taille de la carte sur notre page: elle occupera toute la largeur de la page et aura 800 pixels en hauteur.

<div id= »map »></div>
<div id= »desc »>Cliquez sur la carte pour définir le point de départ – cliquez à nouveau pour définir le point d’arrivée – la route sera tracée</div>
<div align= »center »><img src= »0.png » id= »image » width= »60″ height= »60″></div>
<button id= »clear »>Effacer</button>

Nous plaçons donc le div de la carte, en dessous une ligne de texte qui nous servira comme zone de messages, puis une image pour indiquer l’état de transfert et tout en dessous un bouton Effacer qui permettra de réinitialiser la page. L’image 0.png est une image vide.

Nous retrouvons la référence à la bibliothèque openlayers utilisée:

<script src= »http://openlayers.org/en/v3.16.0/build/ol.js »></script>

et ensuite la création de notre carte :

 var map = new ol.Map({
    target: ‘map’,
    layers: [   new ol.layer.Tile({
        source: new ol.source.OSM() }) ],
    view: new ol.View({
      center: [-500000, 6172000],
      zoom: 12 }),
    controls: ol.control.defaults({
      attributionOptions: {
        collapsible: false  }  }) });

Nous chargeons dans notre carte la couche OpenStreetMaps, nous centrons l’affichage sur les coordonnées EPSG:3857 du centre de notre zone d’intérêt, nous définissons l’étendue affichée au départ avec le facteur de zoom (12) et finalement les contrôles souhaités.

La suite du code traite les points de départ et d’arrivée.

Nous créons deux entités (features) pour accueillir ces points

// Les points de départ et d’arrivée.
var startPoint = new ol.Feature();
var destPoint = new ol.Feature();

Puis nous définissons une couche vecteur pour les afficher

// La couche vecteur utilisée pour afficher les points de départ et d’arrivée.
var vectorLayer = new ol.layer.Vector({
source: new ol.source.Vector({
features: [startPoint, destPoint]})
});

map.addLayer(vectorLayer);

Nous créons une variable params avec certains paramètres fixes de notre requête à envoyer à Geoserver, le nom de a couche Geoserver à interroger et le format sous lequel nous voulons recevoir le résultat.

var params = {
LAYERS: ‘pgrouting:itineraire’,
FORMAT: ‘image/png’
};

Puis nous défissons une fonction de transformatons de coordonnées. OpenStreetMap travaille en coordonnées sphériques Pseudo-Mercator (EPSG:3857). Mais nos données PostGis sont en coordonnées géographiques (EPSG:4326).

Quand nous allons récupérer le clic sur la carte, les coordonnées seront en 3857. Nous devrons es transformer en 4326 avant d’envoyer la requête vers Geoserver.
// La fonction de transformation des coordonnées à partir de l’EPSG:3857
// vers  EPSG:4326.
var transform = ol.proj.getTransform(‘EPSG:3857’, ‘EPSG:4326’);

Maintenant nous mettons en place l’écoute de l’événement click sur notre carte. Cette partie du code sera activée quand l’utilisateur cliquera sur la carte.

// Mise en place d’une écoute sur les évenements de la carte.
map.on(‘click’, function(event) {

Si le startpoint est vide, cela indique que c’est le premier click sur la carte et que c’est le point de départ qui doit être renseigné.
if (startPoint.getGeometry() == null) {
// Premier click.
startPoint.setGeometry(new ol.geom.Point(event.coordinate));

Si, par contre, le startpoint est déjà rensigné, nous sommes en présence du deuxième click. Nous allons donc renseigner le destPoint :
} else if (destPoint.getGeometry() == null) {
// Deuxième click.
destPoint.setGeometry(new ol.geom.Point(event.coordinate));

Et nous enchaînons directement sur le processus de soumission de la requ^ùete à Geoserver.

Nous transformons les coordonnées des deux points cliqués:
// Transformations des coordonnées à partir de celle de la carte (EPSG:3857)
// vers celle du serveur (EPSG:4326).
var startCoord = transform(startPoint.getGeometry().getCoordinates());
var destCoord = transform(destPoint.getGeometry().getCoordinates());

et nous construisons la chaîne de caractères avec les paramètres x1, y1,x2 et y2 nécessaires à  notre couche Geoserver (voir l’article précédent)
var viewparams = [
‘x1:’ + startCoord[0], ‘y1:’ + startCoord[1],
‘x2:’ + destCoord[0], ‘y2:’ + destCoord[1]
];

Voilà, nous avons tous les éléments nécessaires pour construire le message à envoyer à Geoserver. Ce mesage nous le mettons dans une variable source.

params.viewparams = viewparams.join(‘;’);
var source =new ol.source.ImageWMS({
url: ‘http://localhost:8080/geoserver/pgrouting/wms’,
params: params

Et nous envoyons la requête vers Geoserver en demandant de mettre le résultat dans une nouvelle couche Image (nous avons demandé le retour sous forme d’image png un peu plus haut) à afficher dans notre map

result = new ol.layer.Image({
source: source

Comme l’interrogation n’est pas instantanée, il vaut mieux tenir au courant l’utilisateur de ce qui se passe sans l’obliger à rester devant un écran semblant figé.

Les lignes suivantes affichent un texte de message et une image montrant que la requête est en cours, puis quand le transfert est terminé, indiquant s’il ‘est bien passé ou non.

source.on(‘imageloadstart’, function() {
document.getElementById(« desc »).innerHTML = »chargement… »;
document.getElementById(« image »).src = »tree_loading.gif »;
});
source.on(‘imageloadend’, function() {
document.getElementById(« desc »).innerHTML = »terminé OK! »;
document.getElementById(« image »).src = »OK.jpg »;
});
source.on(‘imageloaderror’, function() {
document.getElementById(« desc »).innerHTML = »erreur! »;
document.getElementById(« image »).src = »no.png »;
});

Nous chargeons la couche dans notre carte:

map.addLayer(result);

Finalement, nous traitons le click sur le bouton Effacer:

var clearButton = document.getElementById(‘clear’);
clearButton.addEventListener(‘click’, function(event) {
// Réinitialise les entités  « start » et « destination » .
startPoint.setGeometry(null);
destPoint.setGeometry(null);
document.getElementById(« desc »).innerHTML = »Cliquez sur la carte pour définir le point de départ – cliquez à nouveau pour définir le point d’arrivée – la route sera tracée »;
document.getElementById(« image »).src = »0.png »;
// Remove the result layer.
map.removeLayer(result);
});

En vidant les entités startPoint et destPoint, en remettant le message d’accueil et l’image vide et en effaçant la couche vecteur contenat l’itinéraire précédent.

Nous pouvons ouvrir cette page dans notre navigateur et voir l’application tourner:

Si cet article vous a intéressé et que vous pensez qu'il pourrait bénéficier à d'autres personnes, n'hésitez pas à le partager sur vos réseaux sociaux en utilisant les boutons ci-dessous. Votre partage est apprécié !

7 thoughts on “Développer une application avec pgrouting sous Windows (7):OpenLayers 3

  1. > Nous arrivons à la fin de cette série.
    Qui vraiment mérite un public plus vaste qui ne soit pas réduit aux utilisateurs Windows : hormis quelques détails d’installation dans le premier article, tout est valable pour Linux. Je plaide fortement pour un renommage de la série en « Développer une application avec pgrouting » avec un avertissement pour le premier article.

    Suivent quelques typo relevées.

    > et ensuite l création de notre carte:
    et ensuite la création de notre carte :
    (d’autres textes français ont un deux-points collé au texte, en typographie française les doubles signes de ponctuation sont précédés d’une espace insécable).

    > center: [-500000, 6172000],
    Les coordonnées Pseudo Mercator ne sont pas parlantes, autant les donner en lat/lon avec la reprojection comme indiquée plus loin (enfin la projection inverser).

    > coordonnées Mercator sphériques
    plus précisément coordonnées sphériques Pseudo-Mercator car Mercator n’est pas sphérique.

    > Si le startpoint est vide, cel indique que c’est le premier clcick sur la carte et que c’est le point de départ qui doit être renseigné.
    cela, clic

    > Et nous enchaînos directement sur le processus de souission de la requ^te à Geoserver.
    enchaînons, soumission, requête

    Trop pressé ou relecture non sauvegardée ? Ça dévalorise cet article très didactique.

  2. Salut! J’ai suivi votre tutoriel mais ça ne marche pas. Il n’affiche que le couche source OSM, pouvez vous donnez le fichier de code source complet pour cette partie ? S’il vous plaît!

    1. le code est dans l’article: cliquez sur le + à droite de « Texte de la page html » (en début de l’article). Attention à ne pas faire du copier-coller de ce texte sans corriger les  » et les ‘. Malheureusement, la mise en forme automatique du texte html avec WordPress les remplace par d’autres caractères.

        1. il faut chercher pas à pas. Depuis votre code jusqu’à la fonction postgis. Affichez la console web de votre page html, récupérez l’appel à geoserver, puis exécutez-le sur une nouvelle page. Si geoserver renvoi un message d’erreur vous le verrez dans cette page.
          Testez votre sql de la couche geoserver en remplaçant les x1,Y1, X2;, Y2 par les valeurs de l’appel.
          Testez votre fonction postgis avec ces valeurs…
          Bref, il faut valider chacune des étapes jusqu’à trouver pourquoi vous n’obtenez pas le résultat attendu.

          1. Bonjour ! j’ai un erreur dans le consol si j’ai cliqué sur la carte :
            « TypeError: startPoint.getGeometry(…) is undefined », qu’est ce que ça peut être ?

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *