V1V2
Blog & Projects

Tutoriel de géolocalisation en HTML5

Terre

Ce tutoriel a pour but de mettre en pratique pas à pas la géolocalisation en HTML5. Nous allons voir comment il est possible de récupérer les coordonnées d'un utilisateur, comment gérer les cas de succès et d'erreur avec les callbacks, comment suivre ses déplacements, et enfin toutes les informations que nous pouvons obtenir sur ses coordonnées, comme sa vitesse ou sa direction.

Avant de commencer il est important de rappeler qu'à l'heure actuelle, la géolocalisation n'est pas encore supportée par Internet Explorer. En revanche, tous les autres navigateurs modernes l'implémentent correctement. Il est possible de suivre l'évolution des fonctionnalités HTML5 prises en charge par les différents navigateurs sur CanIUse.com ou bien le Wiki des moteurs de rendu utilisés par les navigateurs.

Récupérer les coordonnées

Tout d'abord, nous allons créer un document HTML le plus simple possible, sans se soucier du Doctype, de la partie head et des autres détails de la structure HTML habituelle. Nous avons uniquement besoin d'exécuter du code JavaScript. Voilà donc à quoi ressemble notre fichier :

<script type="text/javascript">

</script>

Avant de chercher à utiliser la géolocalisation, il est bien entendu important de savoir si le navigateur de l'utilisateur la supporte. Pour cela on ajoute simplement les lignes :

if (navigator.geolocation) {
  alert('Bravo ! Votre navigateur prend en compte la géolocalisation HTML5')
} else {
  alert('Dommage... Votre navigateur ne prend pas en compte la géolocalisation HTML5')
}

navigator.geolocation renvoie un simple booléen valant vrai ou faux selon la capacité du navigateur à utiliser la géolocalisation (...autrement dit c'est un détecteur d'IE !). Ensuite, afin de récupérer les coordonnées, on utilise getCurrentPosition() :

if (navigator.geolocation) {
  navigator.geolocation.getCurrentPosition(function (position) {
    alert('Latitude : ' + position.coords.latitude + ', longitude : ' + position.coords.longitude)
  })
} else {
  alert('Votre navigateur ne prend pas en compte la géolocalisation HTML5')
}

Cette méthode prend en paramètre une fonction de callback qu'elle va appeler en lui fournissant le paramètre position contenant les coordonnées. Dans l'exemple ci-dessus, on utilise une fonction anonyme afin de traiter la variable position immédiatement à la suite du code d'appel. Dans le cas où l'on souhaite un traitement plus propre et détaillé, on utilisera une fonction nommée et déclarée comme le montrent les prochains exemples.

Oui, à condition qu'il accepte de partager sa localisation ! Vous avez peut-être déjà remarqué le bandeau qui apparaît en haut de certaines pages (le même bandeau qui vous propose d'enregistrer un mot de passe par exemple). Afin d'accepter de partager sa localisation, un bandeau de ce type apparaît :

Barre d'autorisation de partage de géolocalisation

Sur les smartphones, c'est un pop-up équivalent qui apparaît.

Elle apparaît dès que l'on cherche à accéder aux coordonnées de l'utilisateur. Donc par exemple lorsque vous utilisez getCurrentPosition ! Ce qui évite de demander l'autorisation inutilement à l'utilisateur. Allez, on reprend avec nos callbacks !

Utilisation des callbacks

La méthode getCurrentPosition() peut prendre un second paramètre : le callback d'erreur. C'est la fonction qui sera appelée dans les cas où :

  • L'utilisateur refuse l'autorisation d'accès à sa position,
  • L'emplacement de l'utilisateur n'ait pas pu être déterminé,
  • Le service de géolocalisation ne réponde pas assez vite.

On utilise donc la fonction de callback pour gérer ces 3 cas :

if (navigator.geolocation) {
  navigator.geolocation.getCurrentPosition(successCallback, errorCallback)
} else {
  alert('Votre navigateur ne prend pas en compte la géolocalisation HTML5')
}

function successCallback(position) {
  alert('Latitude : ' + position.coords.latitude + ', longitude : ' + position.coords.longitude)
}

function errorCallback(error) {
  switch (error.code) {
    case error.PERMISSION_DENIED:
      alert("L'utilisateur n'a pas autorisé l'accès à sa position")
      break
    case error.POSITION_UNAVAILABLE:
      alert("L'emplacement de l'utilisateur n'a pas pu être déterminé")
      break
    case error.TIMEOUT:
      alert("Le service n'a pas répondu à temps")
      break
  }
}

Enfin, le 3ème paramètre de getCurrentPosition() sert à indiquer certaines options particulières, écrites de la forme suivante : {nom:valeur, nom:valeur, ...}

On peut ainsi spécifier :

  • L'utilisation du GPS pour obtenir des coordonnées beaucoup plus précises,
  • Un timeout si l'on a besoin d'une réponse avant un certain délai,
  • La durée de vie maximale d'une coordonnée envoyée par l'utilisateur.
navigator.geolocation.getCurrentPosition(successCallback, errorCallback, {
  enableHighAccuracy: true,
  timeout: 10000,
  maximumAge: 600000,
})

Les options timeout et maximumAge sont indiquées en millisecondes, donc ici, on établit un délai de 10 secondes de timeout, et une durée de vie maximale de la dernière position de l'utilisateur de 10 minutes. Par défaut, timeout a pour valeur Infinity, et si maximumAge vaut 0, alors l'application demande systématiquement une mise à jour de la position de l'utilisateur.

Suivre les déplacements

Dans de nombreux cas, on aimerait mettre à jour la position de l'utilisateur si celui-ci se déplace. On pourrait penser qu'une fonctionnalité telle que celle-ci pourrait être assez lourde à mettre en place... ce n'est pas le cas ! Il suffit simplement de remplacer getCurrentPosition() par :

var watchId = navigator.geolocation.watchPosition(successCallback, errorCallback)

Comme on veut permettre à l'utilisateur d'arrêter le suivi de ses déplacements, on ajoute un simple lien effectuant un appel à clearWatch().

function stopWatch() {
  navigator.geolocation.clearWatch(watchId)
}
<a href="#" onclick="stopWatch()">Stop Watch</a>

Enfin, si l'on souhaite pouvoir tester facilement ce code en se promenant dans la rue avec son smartphone, il faut activer l'option enableHighAccuracy pour utiliser le GPS afin d'avoir une mise à jour fréquente de la position. Voici le code JavaScript complet permettant d'afficher une nouvelle popup contenant les coordonnées chaque fois que l'utilisateur se déplace :

if (navigator.geolocation) {
  var watchId = navigator.geolocation.watchPosition(successCallback, errorCallback, {
    enableHighAccuracy: true,
  })
} else {
  alert('Votre navigateur ne prend pas en compte la géolocalisation HTML5')
}

function successCallback(position) {
  alert('Latitude : ' + position.coords.latitude + ', longitude : ' + position.coords.longitude)
}

function errorCallback(error) {
  switch (error.code) {
    case error.PERMISSION_DENIED:
      alert("L'utilisateur n'a pas autorisé l'accès à sa position")
      break
    case error.POSITION_UNAVAILABLE:
      alert("L'emplacement de l'utilisateur n'a pas pu être déterminé")
      break
    case error.TIMEOUT:
      alert("Le service n'a pas répondu à temps")
      break
  }
}

function stopWatch() {
  navigator.geolocation.clearWatch(watchId)
}

C'est court n'est-ce pas ? Et nous avons une gestion des erreurs, ainsi qu'une fonctionnalité d'arrêt de la géolocalisation ! Si on avait voulu simplifier à l'extrême on aurait pu laisser uniquement les lignes vitales :

var watchId = navigator.geolocation.watchPosition(
  function (position) {
    alert('Latitude : ' + position.coords.latitude + ', longitude : ' + position.coords.longitude)
  },
  null,
  { enableHighAccuracy: true }
)

Je ne sais pas ce que vous en dites, mais lorsque je me suis rendu compte que 4 lignes de code JavaScript pouvaient faire fonctioner un suivi de position par GPS, j'ai vraiment été bluffé. Cela montre bien la volonté du W3C de faire de l'HTML5 un nouveau standard simple à utiliser.

La vitesse ! La boussole ! L'altitude !

Si vous avez été charmés par la simplicité par la partie précédente, voilà qui devrait vous achever ! Lorsque l'on récupère la variable position afin d'obtenir la latitude grâce à position.coords.latitude, on a accès à plusieurs autres attributs :

  • position.timestamp, qui renvoie le timestamp de l'heure à laquelle a été mise à jour la position,
  • position.coords.altitude, qui correspond à l'altitude de l'utilisateur,
  • position.coords.accuracy, qui correspond à la précision des coordonnées,
  • position.coords.altitudeAccuracy, qui correspond à la précision de l'altitude,
  • position.coords.heading, qui correspond à l'angle compris entre 0 et 360° par rapport au Nord (ce n'est pas aussi précis que la boussole d'un smartphone),
  • position.coords.speed, qui correspond à la vitesse de l'utilisateur par rapport à sa dernière position.

Toutes ces valeurs sont remplies par le service de géolocalisation et vous n'avez absolument pas besoin de les calculer vous-même !

Code complet de démonstration

Pour terminer, voici le code d'une démonstration des capacités de géolocalisation de l'HTML5. Elle teste la capacité du navigateur à utiliser la géolocalisation, et dès qu'un déplacement est effectué, elle affiche :

  • La latitude de l'utilisateur,
  • La longitude de l'utilisateur,
  • La précision de la latitude et de la longitude,
  • L'altitude de l'utilisateur,
  • La précision de l'altitude,
  • L'angle par rapport au Nord,
  • La vitesse par rapport à la dernière position,
  • La date et l'heure de la dernière position.

Elle est à tester de préférence avec un smartphone équipé d'un GPS.

<!DOCTYPE html>
<html>
  <head>
    <style type="text/css">
      body {
        background-color: #aaf;
      }
      #main {
        width: 400px;
        padding: 30px;
        margin: auto;
        background-color: #eee;
      }
      ul {
        list-style-type: none;
      }
      h1 {
        margin-top: 0;
      }
    </style>
    <script type="text/javascript">
      function startWatch() {
        if (navigator.geolocation)
          var watchId = navigator.geolocation.watchPosition(successCallback, errorCallback, {
            enableHighAccuracy: true,
            timeout: 10000,
            maximumAge: 0,
          })
        else alert('Votre navigateur ne prend pas en compte la géolocalisation HTML5')
      }

      function stopWatch() {
        navigator.geolocation.clearWatch(watchId)
      }

      function successCallback(position) {
        document.getElementById('lat').innerHTML = position.coords.latitude
        document.getElementById('long').innerHTML = position.coords.longitude
        document.getElementById('prec').innerHTML = position.coords.accuracy
        document.getElementById('alt').innerHTML = position.coords.altitude
        document.getElementById('precalt').innerHTML = position.coords.altitudeAccuracy
        document.getElementById('angle').innerHTML = position.coords.heading
        document.getElementById('speed').innerHTML = position.coords.speed
        document.getElementById('time').innerHTML = new Date(position.timestamp)
      }

      function errorCallback(error) {
        switch (error.code) {
          case error.PERMISSION_DENIED:
            alert("L'utilisateur n'a pas autorisé l'accès à sa position")
            break
          case error.POSITION_UNAVAILABLE:
            alert("L'emplacement de l'utilisateur n'a pas pu être déterminé")
            break
          case error.TIMEOUT:
            alert("Le service n'a pas répondu à temps")
            break
        }
      }
    </script>
  </head>

  <body>
    <div id="main">
      <ul>
        <li>Latitude : <span id="lat"></span></li>
        <li>Longitude : <span id="long"></span></li>
        <li>Précision : <span id="prec"></span></li>
        <li>Altitude : <span id="alt"></span></li>
        <li>Précision altitude : <span id="precalt"></span></li>
        <li>Angle par rapport au Nord : <span id="angle"></span></li>
        <li>Vitesse : <span id="speed"></span></li>
        <li>Date - Heure : <span id="time"></span></li>
      </ul>
      <a href="#" onclick="startWatch()">Démarrer</a>
      <a href="#" onclick="stopWatch()">Arrêter</a>
    </div>
  </body>
</html>