Tutoriel : Consommer le service de données OGDI avec jQuery – 3ième partie

Ce billet constitue une extension directe de la seconde partie du tutoriel sur la consommation de service de données OGDI (Open Government Data Initiative) avec jQuery qu’il faut donc avoir couvert avant de commencer cette partie.

La seconde partie se conclut sur des pistes d’extensibilité que nous reprenons ici. L’objectif consiste donc à ajouter une fonctionnalité à l’application permettant de voir les lignes de bus et de tramway de la Communauté Urbaine de Bordeaux (CUB) passant à proximité d’un établissement scolaire quand ce dernier est sélectionné.

Ces lignes de bus sont représentées par un ensemble de points (latitude et longitude) qui, une fois reliés entre eux, représentent le trajet de la ligne de bus.

Pour rappel, l’existant est constitué d’une application exclusivement écrite en JavaScript/HTML utilisant les technologies jQuery pour les appels de service de l’instance de test du kit de démarrage OGDI (Open Government Data Initiative) et la manipulation du DOM et Bing Cartes AJAX 7.0 pour la partie cartographie.

Consommation des données « Lignes de Bus » exposées par le service de données OGDI

A l’image de l’existant, il faut consommer un nouvel ensemble de données depuis l’instance de test OGDI pour afficher les lignes de bus sur la carte Bing Cartes. Il convient donc commencer par faire un appel asynchrone à l’URL https://ogdifrancedataservice.cloudapp.net/v1/frOpenData/LignesBusBordeaux/?&format=json.

A noter que le code est tout à fait factorisable avec le code existant, mais pour des raisons de lisibilité et, étant donné, la nature didactique de ce tutoriel, nous allons détailler le code.

Le code suivant doit être rajouté à la suite de l’existant afin de consommer les données depuis l’instance OGDI et les stocker en tant qu’objets de type Polyline dans un tableau en variable globale ; celui-ci sera accessible depuis les autres fonctions dans la suite de ce tutoriel.

var lines = new Array();

 

$(document).ready(readPolylineData("https://ogdifrancedataservice.cloudapp.net/v1/frOpenData/LignesBusBordeaux/?&format=json"));

 

//Lecture du flux de données OData contenant les lignes de bus

function readPolylineData(requestUrl) {

   $.ajax({ dataType: "jsonp",

            url: requestUrl + "&$callback=polylineCallback",

            success: polylineCallback

   });

}

 

function polylineCallback(data) {

//Extension du prototype des objets de type Polyline pour rajouter des propriétés liées aux lignes de bus.

   MM.Polyline.prototype.numLigne = null;

   MM.Polyline.prototype.nomLigne = null;

   //Pour chaque ligne de bus, on initialise un objet de type Polyline via la fonction initializePolyline

   $.each(data.d, initializePolyline);

}

 

function initializePolyline(i, line) {

   var locations = new Array();

   var rawPolygons = line.polygons;

  

   //Parsing de tous les points composant le polygone

   //(qui est en fait dans le cas présent un trajet de bus, donc une somme de points formant une ligne).

   //Formatage de type 'longitude,latitude,altitude longitude,latitude,altitude etc...)

   var points = rawPolygons.split(' ');

   for (i = 0; i < points.length; i++) {

      //Parsing de chaque point pour récupérer la longitude et la latitude

      var coordinates = points[i].split(',');

      var location = new MM.Location(coordinates[1], coordinates[0]);

      //alimentation du tableau d'objets Location qui va servir à initialiser l'objet de type Polyline

      locations.push(location);

   }

   var polyline = new MM.Polyline(locations, { strokeThickness: 2, visible: false });

   polyline.numLigne = line.nomcomli;

   polyline.nomLigne = line.nomcomch;

   MM.Events.addHandler(polyline, 'mouseover', displayInfoBox);

   lines.push(polyline);

   map.entities.push(polyline);

}

Algorithme d’affichage des lignes de bus les plus proches

Comme vous pouvez le remarquer dans le code précédents, les lignes de bus sont cachées sur la carte (propriété visible à false) dans l’initialisation de l’objet Polyline au niveau de la fonction initializePolyline. Nous avons donc besoin maintenant d’un algorithme pour rendre visibles les lignes de bus dès qu’un lycée est sélectionné et qu’une ligne de bus passe à proximité.

Pour ce faire, nous allons donc écrire une fonction displayNearLines qui va être appelée à chaque clic sur un lycée pour parcourir l’ensemble des lignes de bus stockées dans le tableau lines (en variable globale) et, pour chaque ligne de bus, déterminer si un des points est situé à moins de 100 mètres du lycée en question (via la fonction distance).

//Algorithme d'affichage des lignes de bus si elles passent près d'un établissement scolaire.

function displayNearLines(e) {

   if (e.targetType = "pushpin") {

   for (var i = 0; i < lines.length; i++) {

      lines[i].setOptions({ visible: false });

         var locations = lines[i].getLocations();

         for (var j = 0; j < locations.length; j++) {

            var establishment = e.target.getLocation();

            //Si une ligne de bus passe à moins de 100 mètres d'une école on la rend visible.

  if (distance(establishment.latitude, establishment.longitude, locations[j].latitude, locations[j].longitude, "K") < 0.100) {

               lines[i].setOptions({ visible: true });

               break;

            }

         }

      }

   }

}

 

//Méthode permettant de calculer la distance entre 2 points (représentés par une latitude et une longitude).

function distance(lat1, lon1, lat2, lon2, unit) {

   var radlat1 = Math.PI * lat1 / 180;

   var radlat2 = Math.PI * lat2 / 180;

   var radlon1 = Math.PI * lon1 / 180;

   var radlon2 = Math.PI * lon2 / 180;

   var theta = lon1 - lon2;

   var radtheta = Math.PI * theta / 180;

   var dist = Math.sin(radlat1) * Math.sin(radlat2) + Math.cos(radlat1) * Math.cos(radlat2) * Math.cos(radtheta);

   dist = Math.acos(dist);

   dist = dist * 180 / Math.PI;

   dist = dist * 60 * 1.1515;

   if (unit == "K") { dist = dist * 1.609344; }

   if (unit == "N") { dist = dist * 0.8684; }

   return dist;

}

Enfin, pour appeler la fonction displayNearLines lors d’un clic sur un lycée, il est nécessaire de modifier la fonction déjà existante initializePushpin afin de s’abonner une fois de plus à l’évènement clic de la punaise représentant le lycée.

//Ajout d'une punaise à la carte

function initializePushpin(i, establishment) {

   var pushpin = new MM.Pushpin(new MM.Location(establishment.latitude, establishment.longitude),

          {

          icon: 'Icons/pushpin.png',

             height: 30,

             width: 30

          });

   pushpin.name = establishment.nom;

   pushpin.type = establishment.sstheme;

   pushpinClick = MM.Events.addHandler(pushpin, 'click', displayInfoBox);

   pushpinClick = MM.Events.addHandler(pushpin, 'click', displayNearLines);

   map.entities.push(pushpin);

   pushpins.push(pushpin);

}

Ainsi, lors du clic sur un lycée, une ligne rouge représentant un trajet d’une ligne de bus s’affiche sur la carte (il peut y avoir un délai de l’ordre de 5 secondes le temps que toutes les lignes de bus soient téléchargées).

image

Affichage des informations concernant une ligne de bus

Maintenant que nous pouvons afficher le trajet des lignes de bus, il convient de rajouter une fonctionnalité permettant d’obtenir des informations telles que le numéro et le nom de ligne de bus.

La solution retenue ici consiste à afficher ces informations dans la même boite de dialogue que celle utilisée pour afficher les informations sur le lycée sélectionné dès que la souris passe au-dessus de la ligne de bus. (Bien que ce choix soit discutable en termes d’ergonomie, il se révèle très pratique à mettre en place dans le cadre de ce tutoriel :-)).

Une fonction permettant de suivre la position de la souris nous est nécessaire. Dès que la souris passe au-dessus de la ligne de bus, nous affichons la boite de dialogue près de la position de la souris. Si vous êtes un observateur attentif, vous aurez remarqué que, dans la fonction initializePolyline proposée au tout début de ce tutoriel, nous abonnions la fonction displayInfoBox à l’évènement mouseover des objets Polyline. Nous allons donc étendre cette fonction.

//Tracking de la position de la souris

var mouseX;

var mouseY;

 

$(document).ready(function () {

   $(document).mousemove(function (e) {

   mouseX = e.pageX;

      mouseY = e.pageY;

   })

});

 

//Affichage de la boite de dialogue

function displayInfoBox(e) {

   if (e.targetType == "pushpin") {

   var pix = map.tryLocationToPixel(e.target.getLocation(), MM.PixelReference.control);

 

     var infoTitle = document.getElementById('infoTitle');

     infoTitle.innerHTML = e.target.name;

 

     var infoDescription = document.getElementById('infoDescription');

     infoDescription.innerHTML = e.target.type;

 

  var bingSearch = document.getElementById('bingSearch');

     bingSearch.href = 'https://www.bing.com/search?q=' + e.target.name + ' Bordeaux';

 

     var infobox = document.getElementById('infoBox');

     infobox.style.top = (pix.y - 60) + "px";

    infoBox.style.left = (pix.x + 20) + "px";

   infoBox.style.visibility = "visible";

     document.getElementById('myMap').appendChild(infoBox);

   }

     

   if (e.targetType == "polyline") {

      var infoTitle = document.getElementById('infoTitle');

      infoTitle.innerHTML = "Ligne " + e.target.numLigne;

      var infoDescription = document.getElementById('infoDescription');

      infoDescription.innerHTML = e.target.nomLigne;

      var bingSearch = document.getElementById('bingSearch');

      bingSearch.href = 'https://www.bing.com/search?q=' + e.target.nomLigne;

      var infobox = document.getElementById('infoBox');

      infobox.style.top = (mouseY + 20) + "px";

      infoBox.style.left = (mouseX + 20) + "px";

      infoBox.style.visibility = "visible";

      $('body').append(infoBox);

}

}

Ainsi, il vous suffit de passer la souris sur une ligne de bus pour obtenir des informations la concernant.

image

Et voilà ! Nous venons d’étendre les fonctionnalités de l’application et de voir comment afficher d’autres types d’objet que de simples « punaises » sur une carte Bing Cartes. Le principe d’affichage est le même pour les polygones, si ce n’est le type d’objet et certaines propriétés qui changent évidemment…

Cette application n’est pas forcément encore totalement complète à ce stade. On pourrait notamment imaginer utiliser l’API de géolocalisation présente dans les spécifications HTML5 (dont une présentation a été faite lors de l’évènement MIX11 au printemps dernier, la vidéo est disponible ici) pour obtenir le meilleur trajet pour se rendre de notre position actuelle à l’établissement scolaire de notre choix à la manière un GPS, mais en utilisant seulement les transports en commun. Si vous souhaitez relever le chalenge, n’hésitez pas !

Source.zip