Marc Silanus

Géolocalisation avec Node Red

Posted on 15 mars 2018

Certain projet peuvent nécessité la mise en œuvre d'une géolocalisation intégrant ou non un capteur GPS. Le but ici est de mettre en place une géolocalisation basée sur l'API de Google à l'aide de Node Red.

API Google Map

Pour commencer, il nous faut obtenir une clé standard pour l'utilisation de l'API Google Map :

Pour utiliser l'API standard, vous devez inclure une clé d'API lors du chargement de l'API.

Avantages de l'utilisation d'une clé d'API :

  • La clé d'API vous permet de surveiller l'utilisation de l'API de votre application dans la Google API Console. Pour plus d'informations, voir l'aide de la Google API Console.

  • Avec une clé, vous avez accès à un généreux quota journalier gratuit et vous avez la possibilité d'augmenter votre quota journalier en activant la facturation à l'utilisation.

  • L'inscription pour obtenir une clé d'API permet à Google de vous contacter à propos de votre application, le cas échéant.

https://developers.google.com/maps/documentation/javascript/get-api-key

  1. Cliquez sur le bouton Obtenir une clé
  2. Créer un nouveau projet
  3. Donnez un nom à votre projet et cliquez sur Next
  4. Cliquez sur le lien API Console pour activer les API
  5. Cliquez sur le menu Bibliothèque
  6. Dans la section Cartes, cliquez sur Tout Afficher
  7. Activez les API Google Places API Web Service,  Google Maps Geocoding API et Google Maps JavaScript API
  8. Sur la page de l'API Google Maps JavaScript API,  cliquez sur le lien Developer documentation
  9. Cliquez sur le lien Créer une carte avec un marqueur
  10. Copiez le code d'exemple dans un fichier texte et enregistrer le sous le nom map.html. Ne pas oublier de remplacer YOUR_API_KEY par votre clé.
    <!DOCTYPE html>
    <html>
      <head>
        <style>
           #map {
            height: 400px;
            width: 100%;
           }
        </style>
      </head>
      <body>
        <h3>My Google Maps Demo</h3>
        <div id="map"></div>
        <script>
          function initMap() {
            var uluru = {lat: -25.363, lng: 131.044};
            var map = new google.maps.Map(document.getElementById('map'), {
              zoom: 4,
              center: uluru
            });
            var marker = new google.maps.Marker({
              position: uluru,
              map: map
            });
          }
        </script>
        <script async defer
        src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&callback=initMap">
        </script>
      </body>
    </html>
    map.html
  11. Déplacer le fichier map.html dans le dossier racine d'un serveur web, par exemple, dans le dossier /home/node-red-static du serveur node red de la Raspberry. (voir dans l'article Tutoriel Node Red) et accédez à cette page dans un navigateur.

L'analyse du code d'exemple fait apparaitre un objet javascript qui est utilisé pour faire passer les coordonnées GPS du centre de la carte et du marqueur :

 var uluru = {lat: -25.363, lng: 131.044};
function initMap() {
        var uluru = {lat: -25.363, lng: 131.044};
        var map = new google.maps.Map(document.getElementById('map'), {
          zoom: 4,
          center: uluru
        });
        var marker = new google.maps.Marker({
          position: uluru,
          map: map
        });
      }
  • Le constructeur d'objet google.maps.Map() permet de construire une nouvelle carte.
  • Le constructeur d'objet google.maps.Marker() permet de construire un nouveau marqueur.

Vous pouvez déjà vous amuser à produire des cartes centrées sur le lieu que vous désirez et placer des marqueurs où bon vous semble. Nous allons dans la suite créer une application node red contenant un formulaire de saisie d'adresse d'un lieu et automatiser le processus de géolocalisation.

Google Gécoding API

Pour réaliser notre application, nous aurons besoin de connaitre les coordonnées GPS du lieu à marquer sur la carte. Le plus simple est pourtant de procéder avec une adresse postale. Google fournie une API nommée Geocoding qui de charge de traduire une adresse postale en coordonnées GPS et vice versa et fourni le noeud node red prêt à l'emploi :

Dans le menu de node red, cliquez sur Manage palette, puis recherchez google.

Installez node-red-node-google. Tous les noeuds ne nous serons pas utiles. Vous pouvez désactiver ceux qui ne vous servirons pas si vous ne voulez pas avoir une palette trop chargée.

Le noeud qu'il nous faut est google geocoding. Saisissez le flow suivant :

Le noeud google geocoding est prévu par défaut pour convertir une adresse en coordonnées GPS. Il faut lui fournir en entrée un objet "location" avec un attribut "address" (Voir l'onglet info dans le menu de droite de node red). Il fourni en sortie un objet "location" avec les attributs "lat" et "lon".

On doit donc préparer une adresse sous la forme d'une chaine de caractères comme par exemple : 25 rue de la république, 75000 Paris, France. Préparons un formulaire de saisie :

Configuration du noeud monFormulaire

Le seul champs qui sera exigé est la ville ou le CP. Formatage de la page web dans un Tab nommé "Géolocalisation" et dans un Group nommé "A trouver...".

Il faut maintenant extraire du formulaire la chaine de caractère qui contiendra l'adresse formatée comme attendu par le noeud "Geocode by Address". C'est le rôle de la fonction prepareLocation.

Fonction prepareLocation

return {
    location : {
        address : msg.payload.adresse+", "+msg.payload.ville+", "+msg.payload.pays
        
    }
};
prepareLocation

A partir de cette information, le noeud "Geocode by Address" fournira en sortie la latitude et la longitude du lieu visé. Préparons la chaine de caractère qui contiendra le code HTML de la page fournie par l'API google map.

Fonction prepareHTML

var lat = msg.location.lat;
var lon = msg.location.lon;
var template ="<style>";
template +="    #map {";
template +="        height: 400px;";
template +="        width: 100%;";
template +="       }";
template +="    </style>";

template +="    <div id='map'></div>";
template +="    <script>";
template +="      function initMap() {";
template +="        var uluru = {lat: "+lat+", lng:"+lon+"};";
template +="        var map = new google.maps.Map(document.getElementById('map'), {";
template +="          zoom: 15,";
template +="          center: uluru";
template +="        });";

template +="        var marker = new google.maps.Marker({";
template +="          position: {lat: "+lat+", lng:"+lon+"},";
template +="          map: map";
template +="        });";


       
template +="      }";
template +="    </script>";
template +="    <script async defer";
template +="    src='https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&callback=initMap'>";
template +="   </script>";

var msg = {
    template: template
};


return msg;
prepareHTML

Il n'y a plus qu'à afficher cette page web dans un template

Configuration du noeud Template

Ici, rien à faire sinon régler les paramètres d'affichage Group, Tab et Size. Certains réglages sont également possibles à partir de l'oglet Dashboard situé à droite de l'écran.

Affichage dans le navigateur

Géolocalisation GPS

L'idée est maintenant de fournir à page de localisation les latitude et longitude issue d'un capteur GPS. J'utilise pour cela un capteur GPS Grove : http://wiki.seeed.cc/Grove-GPS/

Le programme est basé sur l'utilisation de la librairie SoftwareSerial pour lire les données transmises par le GPS. Il est connecté ici sur le port D2 du shield Grove soit, les broches 2 et 3 du connecteur Arduino :

/***************************************************************************
*
*  Programme   : GPS.ino
*  Auteur      : M.S
*  Date        : 27/03/2018
*  Rev         : 0.2
*  Description : Lecture des coordonnées GPS. 
*                Retourne la latitude et la longitude
* Protocole    :
*    - Reception : UART 9600,8,1,N,N NMEA protocol
*                  ","  : sépare les informations contenue dans une trame
*                  "\r\n" : fin de trame
*                  Pour lire la lat et la lon, seules les trames $GPGGA
*                  sont utiles : 
*                  $GPGGA,161229.487,3723.2475,N,12158.3416,W,1,07,1.0,9.0,M.0000*18
*                   - Id : $GPGGA
*                   - UTC position : 161229.487
*                   - lat : 3723.2475 (ddmm.mmmm)
*                   - N/S Indicator : N
*                   - lon : 12158.3416 (dddmm.mmmm)
*                   - W/E Indicator : W
*                   - Position indicator : 1  
*                   - Satellites Used : 07
*                   - Horizontal Dilution of Precision : 1.0
*                   - MSL Altitude : 9.0
*                   - Units : M (meters)
*                   - Checksum : *18
*                 Tous les détails : 
*    https://raw.githubusercontent.com/SeeedDocument/Grove-GPS/master/res/E-1612-UB_Datasheets_Sheet.pdf  
*    
*    - Transmission :
*        * lat,lon\n : lat et lon sont des nombres flotants avec une précision
*                      7 chiffres derrière la virgule.
* Matériel     :
*    - 1 capteur GPS grove http://wiki.seeed.cc/Grove-GPS/
*    
* Remarque : 
*   On ne tient pas compte des indicateur de position N/S et W/E pour une 
*   utilisation dans les hémispères N et E.
*   Sinon, il faut ajouter un signe moins à la latitude et/ou la longitude
*
********************************************************************************/
#include <SoftwareSerial.h>

// Connexion du GPS sur les broches 2 et 3
SoftwareSerial myGPS(2, 3); // RX, TX

String trame; // contient le trame en provenance du GPS

int comma = 0; // contient la position de la virgule dans la trame
String data[11]; // contient les informations contenues dans la trame

void setup() {
  Serial.begin(9600);
  myGPS.begin(9600);
}

void loop() { // run over and over
  // Si des données sont reçues depuis le GPS
  if (myGPS.available()) 
  {
    // Lire une trame complète
    trame = myGPS.readStringUntil('\n');
    // Identifier la trame 
    int firstComma = trame.indexOf(","); // position de la première virgule
    String idMessage = trame.substring(0, firstComma);

    // Si tramme $GPGGA
    if (idMessage == "$GPGGA")
    {
      //Serial.println(trame);
      
      // Parcourir trame jusqu'au dernier champs
      for (int i = 0; i < trame.lastIndexOf(","); i++)
      {
        // position de la prochaine virgule
        comma = trame.indexOf(",", comma + 1);
        //Serial.println("position virgule : "+String(comma));

        // acquision des informations contenues dans la trame
        data[i]= trame.substring(comma+1, trame.indexOf(",",comma+1));

        // data[1] : contient la latitude
        // data[3] : contient la longitude
      }

      // calcul des lat et lon en degré décimal
      
      double lat, lon, deg, mn; 
      String str_mn;
      
      // format latitude : ddmm.mmmm
      // lecture des degrés
      deg = data[1].substring(0,2).toInt();
      //Serial.println(deg);

      // lecture de minutes et supression du point
      str_mn = data[1].substring(2);
      str_mn.replace(".","");
      // Conversion en degrés : 60 min = 1 degré 
      // plus de point : mn entier x 10000
      mn =  str_mn.toInt()/600000.0;  
      lat = deg + mn;
      
      // Conversion en chaine de caractère avec la bonne précision :
      // 10 caractères dont 7 après la virgules
      char c_lat[10];
      dtostrf(lat,10,7,c_lat)  ;
      Serial.print(c_lat);

      // format longitude : dddmm.mmmm
      // lecture des degrés
      deg = data[3].substring(0,3).toInt();
      //Serial.println(deg);

      // lecture de minutes et supression du point
      str_mn = data[3].substring(3);
      str_mn.replace(".","");
      // Conversion en degrés : 60 min = 1 degré 
      // plus de point : mn entier x 10000
      mn =  str_mn.toInt()/600000.0;  
      lon = deg + mn;

      // Conversion en chaine de caractère avec la bonne précision :
      // 10 caractères dont 7 après la virgules
      char c_lon[10];
      dtostrf(lon,10,7,c_lon)  ;
      
      Serial.print(",");
      Serial.print(c_lon);

      // Fin de transmition
      Serial.print("\n");

      // attendre 5s avant la prochaine lecture
      delay(5000);
    }
  comma=0;    
  }
  
}
gps.ino

On va ensuite connecter la carte Arduino sur un port USB de la Raspberry Pi et lire les données en provenance du port série, vraisemblablement /dev/ttyACM0

Le programme Arduino fourni les info lat,lon\n toutes les 5 secondes. Faire Ctrl - C pour interrompre.

Nous allons maintenant réaliser le flow node red qui va lire les données issues du port série, les séparer et les transmettre à l'API de google pour afficher une carte centrée sur les coordonnées GPS et y positionner un marqueur.

Configuration du noeud Serial In

Fonction extractLatLon

var o = {
    lat : msg.payload.split(",")[0],
    lon : msg.payload.split(",")[1]
};

return o;
extractLatLon

Fonction prepareHTML

var lat = msg.lat;
var lon = msg.lon;
var template ="<style>";
template +="    #map {";
template +="        height: 600px;";
template +="        width: 100%;";
template +="       }";
template +="    </style>";

template +="    <div id='map'></div>";
template +="    <script>";
template +="      function initMap() {";
template +="        var uluru = {lat: "+lat+", lng:"+lon+"};";
template +="        var map = new google.maps.Map(document.getElementById('map'), {";
template +="          zoom: 20,";
template +="          center: uluru";
template +="        });";

template +="        var marker = new google.maps.Marker({";
lat1 = lat;
lon1 = lon;
template +="          position: {lat: "+lat1+", lng:"+lon1+"},";
template +="          map: map";
template +="        });";


       
template +="      }";
template +="    </script>";
template +="    <script async defer";
template +="    src='https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&callback=initMap'>";
template +="   </script>";

var msg = {
    template: template
};


return msg;
prepareHTML

Le facteur de zoom 20 est celui qui me fourni la meilleure vue sur le quartier. A modifier en fonction de la précision souhaité dans la vue.

Configuration du noeud template

Ici, rien à faire sinon régler les paramètres d'affichage Group, Tab et Size. Certains réglages sont également possibles à partir de l'oglet Dashboard situé à droite de l'écran.

Affichage dans le navigateur

La précision n'est pas toujours au rendez-vous, surtout si vous êtes à l'intérieur. J'obtiens tout de même un positionnement précis à une dizaine de mètres environ.

Commentaires (0) Trackbacks (0)

Désolé, le formulaire de commentaire est fermé pour le moment

Trackbacks are disabled.