Importer une trace GPX dans une base de données avec Leaflet (JS/PHP)

L’objectif est d’insérer la trace d’un fichier GPX dans une base de données (ici MySQL mais le principe est le même pour  PostGIS ou Oracle).

Le résultat final est ici. Le code complet est

import_gpx

Les ingrédients pour cette recette

Vous allez également avoir besoin de quelques ustensiles :

  • Du coté Javascript on utilisera du Jquerry pour simplifier l’Ajax
  •  Du PHP pour traiter les requêtes
  • Et bien sûr, une base de données pour stocker tout ça au frais

Commençons par la création de la table qui recevra les données, voici la requête SQL :

CREATE TABLE `gpx` (
`id` int(11) NOT NULL AUTO_INCREMENT,
  `nom` varchar(200) DEFAULT NULL,
  `distance` int(12) DEFAULT NULL,
  `d_plus` int(12) DEFAULT NULL,
  `d_moins` int(12) DEFAULT NULL,
  `type_trace` varchar(100) DEFAULT NULL,
  `origine` varchar(100) NOT NULL,
  `date_trace` date NOT NULL,
  `descriptionn` text,
  `geom` geometry NOT NULL,
  PRIMARY KEY (`id`)
)
Création de la table

Nous avons donc notre table ‘gpx’ contenant 11 champs.

Le champ ‘id’ est la clé primaire, le champ ‘geom’ recevra la géométrie, les champs ‘distance’, ‘d_plus’, ‘d_moins’ (dénivelé positif et négatif) et nom seront déduis automatiquement à partir du fichier GPX. Le reste des champs  seront remplis par l’utilisateur.

 

Tout est prêt, entrons donc dans le vive du sujet.

Commençons par créer un petit fichier php qui va contenir les paramètres de configuration de l’application (connexion à la bdd, mot de passe de l’application)

<?php
$password_attendu = 'demo';
  
$cfg_db_host = 'localhost';
$cfg_db_db = 'db_gpx';
$cfg_db_user = 'root';
$cfg_db_password = '';
?>
config.php

On va créer une toute petite page d’authentification pour que tout le monde ne puisse pas accéder à l’application. Il n’y aura qu’un champ pour entrer le mot de passe et un bouton pour valider. Ce sera la page de démarrage, nous allons donc l’appelé ‘index.html’.

<!DOCTYPE html>
<html>
<head>
  <title>Importer les GPX</title>
  <meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />
                 <link rel="stylesheet" href="/index.css" />
</head>
<body>
<div id = "main" >
 <div id ="connexion"> <h1>Importer des GPX</h1> </div>
  <form name="formulaire" action="php/login.php" method="post">
                <p>Mot de passe : <input type="password" name="password" value = 'dada'/></p>
                <input id="btn" type="submit" value="Connexion">
  </form>
  </div>
</body>
</html>
index.html

C’est la page ‘login.php’ qui va traiter la requête, voici le code commenté :

<?php
include 'config.php';
  
// on teste si nos variables sont définies
if ( isset($_POST['password'])) {
  
                               // on vérifie les informations du formulaire (ici que le mot de passe est le bon)
                               if ($password_attendu == $_POST['password']) {
                                                               // dans ce cas, tout est ok, on peut démarrer notre session
                                                               session_start ();
                                                               // on enregistre mot de passe comme variables de session
                                                               $_SESSION['password'] = $_POST['password'];
  
                                                               // on redirige notre visiteur vers la page principale
                                                               header ('location: ../main.php');
                               }
                               else {
                                               // Le mot de passe est incorrect. On utilise alors un petit javascript lui signalant ce fait
                                               echo '<body onLoad="alert(\'Mauvais mot de passe\')">';
                                               // puis on le redirige vers la page d'accueil
                                               echo '<meta http-equiv="refresh" content="0;URL=../index.html">';
                               }
}
else {
                               echo 'Les variables du formulaire ne sont pas déclarées.';
}
?>
login.php

Passons maintenant à la page principale.

Elle va être constitué d’une carte sur la droite et d’une barre verticale à gauche permettant d’ouvrir le GPX de renseigner les données et de valider tout ça pour l’envoyer dans la BDD

<?php
include 'php/config.php';
session_start (); 
if ($_SESSION['password'] != $password_attendu ){ // si le mot de passe stocké dans la session est le bon, on continue sur cette page, sinon on le reirige vers la page d'accueil
header("location:index.html");
}
?>
  
<!DOCTYPE html>
<html>
<head>
  <title>Importer les GPX</title>
    
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <meta name="viewport" content="width=1024, user-scalable=no">
  
                <link rel="stylesheet" href="/style_main.css" />
    <link rel="stylesheet" href="/leaflet.css" />
                
<!--[if lte IE 8]>
    <link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.6.4/leaflet.ie.css" />
<![endif]-->
  
 <script src="/lib/leaflet.js"></script>
 <script src="/lib/gpx.js"></script>
 <script src="/lib/LeafletToWKT.js"></script>
  <script src="/lib/jquery.js"></script>
<div id="bloc_page">
 <div id="barre_droite">
   
 <!-- le bouton parcourir -->
                               <input id="file" type="file" onchange="load_file();" />
                               
 <!-- les formulaires-->
                <form name="formulaire">
                                               <label for="nom_fichier">Nom du fichier :</label>
                                               <input type="text" name="nom_fichier" value="" disabled>
                
                                               <label for="distance">Distance :</label>
                                               <input type="text" name="distance" value="">
                
                                               <label for="D+">D+ :</label>
                                               <input type="text" name="D+" value="">
                
                                               <label for="D-">D- :</label>
                                               <input type="text" name="D-" value="">
                                               
                                               <label for="decription">Description :</label>
                                               <input type="text" name="description" value="">
                                               
                                               <label for="origine">Origine du fichier :</label>
                                               <input type="text" name="origine" value="">
                                               
                                               <label for="date_trace">Date de la trace :</label>
                                               <input type="date" name="date_trace" value="">
                                               
                                               <label for="type_activite">Type d'activité :</label>
                               <SELECT id="type_activite" name="type_activite">
                                                               <OPTION VALUE="Pedestre">Pedestre</OPTION>
                                                               <OPTION VALUE="VTT">VTT</OPTION>
                                                               <OPTION VALUE="Equestre">Equestre</OPTION>
                                                               <OPTION VALUE="Chemin d acces">Chemin d'accès</OPTION>
                                                               <OPTION VALUE="Trail">Trail</OPTION>
                                                               <OPTION VALUE="Raquette">Raquette</OPTION>
                                                               <OPTION VALUE="Ski de fond">Ski de fond</OPTION>
                                                               <OPTION VALUE="Musher">Musher</OPTION>
                               </SELECT>
</form>
 <!-- Le bouton envoyer qui va valider les données et les traités -->
                                               <button  id = 'btn_envoyer' onclick="envoyer_bdd()">Envoyer</button>
</div> 
  
<div id="map" ></div>
   
                
  <script type="text/javascript">
  
  /* --------- initialisation des variable----------*/
  data_obj = new Object();
  feat_gpx = '';
  wkt = "";
  m = "";
  
  
lat_start = 44.8;
long_start = 5.2;
zoom_start = 8;
  
  
/* ---------  création de la carte Leaflet----------*/
var mapQuestAttr = 'Tiles Courtesy of <a href="http://www.mapquest.com/">MapQuest</a> &mdash; ';
var osmDataAttr = 'Map data &copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors';
var mopt = {
    url: 'http://otile{s}.mqcdn.com/tiles/1.0.0/osm/{z}/{x}/{y}.jpeg',
    options: {attribution:mapQuestAttr + osmDataAttr, subdomains:'1234'}
  };
  
var mq=L.tileLayer(mopt.url,mopt.options);
  
 m = L.map('map', {
    center: new L.LatLng(lat_start, long_start), // centre de la carte en WGS84
    zoom: zoom_start, // zoom de départ
                layers: [mq]
});
  
  
  /* --------- fonction qui reset les variable (vu carto, reset des formulaires, etc ----------*/
 function reset(){
m.removeLayer(feat_gpx);
feat_gpx = '';
data_obj = new Object(); // objet ou seront stocké les données en vu de les envoyer vers la bdd
  
                                               document["formulaire"].elements["distance"].value= "";
                                               document["formulaire"].elements["D+"].value= "";
                                               document["formulaire"].elements["D-"].value= "";
                                               document["formulaire"].elements["nom_fichier"].value= "";
                                               document["formulaire"].elements["origine"].value="";
                                               document["formulaire"].elements["date_trace"].value="";
                                               document["formulaire"].elements["description"].value="";
                                               m.setView(new L.LatLng(lat_start, long_start), zoom_start);
                                               
}
  
  /* --------- chargement du fichier----------*/
 fileInput =$('#file')[0];
  
function load_file() {
                               reset();
        var reader = new FileReader();
        reader.onload = function() {
                               data_obj['gpx_full'] = reader.result;
                               data_obj['nom'] = fileInput.files[0].name;
                               document["formulaire"].elements["nom_fichier"].value= data_obj['nom'];
  
                               exec_gpx(m,data_obj.gpx_full);
       
        };
        reader.readAsText(fileInput.files[0]);
                               
   };
  
     /* --------- Le GPX est chargé, on execute cette fonction pour ajouter la trace à la carte, pré remplir les formulaires et stocké la géométrie----------*/
feat_gpx =  new L.featureGroup(); // ou va etre stocké la trace gpx ( de type L.featureGroup)
function exec_gpx(_map,_gpx) {
  
       new L.GPX(_gpx, {
          async: true,
          marker_options: {
            startIconUrl: 'image/pin-icon-start.png', // icone du départ
            endIconUrl:   'image/pin-icon-end.png', // icone de l'arrivé
            shadowUrl:    'image/pin-shadow.png', // ombre des icones
          },
        }).on('loaded', function(e) {
          feat_gpx = e.target; // on stock la géométrie de la trace dans notre variable
                               //console.log( feat_gpx); // pour debug
          _map.fitBounds(feat_gpx.getBounds()); // On zoom sur l'emprise de la trace
                                               
                                               data_obj['distance'] = feat_gpx.get_distance().toFixed(0);
                                               data_obj['dev_cum_pos'] = feat_gpx.get_elevation_gain().toFixed(0);
                                               data_obj['dev_cum_neg'] = feat_gpx.get_elevation_loss().toFixed(0);
                                               
                                               document["formulaire"].elements["distance"].value= data_obj.distance;
                                               document["formulaire"].elements["D+"].value= data_obj.dev_cum_pos;
                                               document["formulaire"].elements["D-"].value= data_obj.dev_cum_neg;
                                               
  
                                                                              // conversion en WKT
                               var feature = feat_gpx.getLayers();
                               feature = feature[0].getLayers()[0];
                               data_obj['wkt'] = toWKT(feature); // on stocke le WKT dans l'objet final
                                               
        }).addTo(_map);
                               
                }
  
  
 /* --------- On click sur envoyer, les données sont traité par 'send_to_bdd.php' qui retourne un tableau JSON pour indiqué ce qu'il s'est passé----------*/
function envoyer_bdd(){
  
 $.ajax({
           type: "POST",
            url: "php/send_to_bdd.php",
                                               
            data:
                                               "&nom="+ data_obj.nom +
                                               "&distance=" +  document["formulaire"].elements["distance"].value +
                                               "&d_plus=" + document["formulaire"].elements["D+"].value +
                                               "&d_moins=" +document["formulaire"].elements["D-"].value +
                                               "&type_activite=" +document["formulaire"].elements["type_activite"].value+
                                               "&origine=" +document["formulaire"].elements["origine"].value+
                                               "&date_trace=" +document["formulaire"].elements["date_trace"].value+
                                               "&description=" +document["formulaire"].elements["description"].value+
                                               "&wkt=" +data_obj.wkt ,
                                               
            success: result, // si tout s'es bien passé, on execute la fonction "result"
            dataType: "json"
            });
  
                                               
                                               function result(data){
  
                               if (data.statut == 1) { // tout s'est bien passé, on l'annonce à l'utiliateur et on reset tout
                                               alert(data.retour);
                                               reset();
                                               clear_file();
                               }
                                if (data.statut  ==2 ||data.statut  ==3 || data.statut  ==4 || data.statut  ==5 ){ // là y'a un problème...
                               alert(data.retour);
                               }
                
  
                                               }
  
  
}
  
// pour reinitialiser le bouton parcourir, mais ne fonctionne pas sur IE...
function clear_file(){
fileInput = document.getElementById("file");
            fileInput.value = "";
            fileInput.focus();
                                               
}
  
</script>
       
        </div>
    </body>
</html>
main.php

Et voici le code commenté de la page de traitement de l’envoi des données :

<?php
// fichier de config où se trouve le mot de passe et les paramètres de connexion à la bdd
include '../php/config.php';
  
// récupération des variables
$nom = $_POST['nom'];
$distance = $_POST['distance'];
$d_plus = $_POST['d_plus'];
$d_moins = $_POST['d_moins'];
$wkt = $_POST['wkt'];
$type_activite = $_POST['type_activite'];
$date_trace = $_POST['date_trace'];
$origine = $_POST['origine'];
$description = $_POST['description'];
  
//regexp pour verifier que la géométrie en wkt est bien une linestring
if (preg_match("/^LINESTRING\(/", $wkt)){ // c'est un WKT linestring
//si c'est le cas on tente de pousser les données dans la base
push_to_bdd();
}
else{
//sinon on indique à l'utilisateur que la géométrie pose problème
echo json_encode(array("retour"=>'aucun tracé chargé ou la géométrie pose problème',"statut"=>2));
die();
}
  
// la fonction principale qui tente de pousser les données dans la base
function push_to_bdd(){
// les variables distantes deviennent globales pour être accessible dans la fonction
global $nom,$distance,$d_plus,$d_moins,$wkt,$type_activite,$origine,$date_trace,$description ;
global $cfg_db_db,$cfg_db_user,$cfg_db_password, $cfg_db_host;
  
// on tente de se connecter à la base
try{
    $bdd = new PDO('mysql:host='.$cfg_db_host.';dbname='.$cfg_db_db, $cfg_db_user , $cfg_db_password);
                }
catch (Exception   $e){
                               echo json_encode(array("retour"=>'Erreur de connexion à la bdd',"statut"=>3, "e" => $e ));
                               die();
}
                // on débute la transaction de la requete paramétrée
try {
                $bdd->beginTransaction();
                // on prepare la requete (transformation de la date(en string) en type date et de la géometrie en WKT en type geométrie
                $req = $bdd->prepare("INSERT INTO gpx(nom, distance, d_plus, d_moins, type_trace, origine, date_trace, descriptionn, geom) VALUES(:nom, :distance, :d_plus, :d_moins,:type_activite,:origine,STR_TO_DATE(:date_trace,'%Y-%m-%d'),:description, GeomFromText(:geom,4326))");
  
                // on execute la requete avec les paramètres que l'on défini
                $req->execute(array(
                               'nom' =>   utf8_decode( $nom),
                               'distance' =>  $distance,
                               'd_plus' =>  $d_plus,
                               'd_moins' =>  $d_moins,
                               'type_activite' =>  utf8_decode($type_activite),
                               'origine' =>  utf8_decode($origine),
                               'date_trace' =>  $date_trace,
                               'description' =>  utf8_decode($description),
                               'geom' =>   $wkt
                ));
  
                // on récupère l'id du dernier enregistrement
   $last_insert = $bdd->lastInsertId();
  
   // si ce dernier est = à 0, il y a un problème
   if ($last_insert == 0){
     echo json_encode(array("retour"=>"Une erreur lors de l'insert s'est produite","statut"=>5));
   }
   
   //sinon tout va bien
   else {
    $bdd->commit();
                   echo json_encode(array("retour"=>"Import de ".$nom. " réussi","statut"=>1));
                               }
                }
                
                // si on a une erreur dans la transaction
                catch(PDOExecption $e) {
                               echo json_encode(array("retour"=>"Erreur lors de l'execution de la requete","statut"=>4));
                               $bdd->rollback();
                               die();
    }
} //EOF la fonction push to bdd
                
?>
send_to_bdd.php

Et voilà.

Pour consulter les traces, nous pouvons nous connecter directement à la base (en lecture seule) via QGIS par exemple.

Le prochain tuto permettra d’afficher ces GPX via très certainement Openlayers 3 histoire de varier les plaisirs.

7 pensées sur “Importer une trace GPX dans une base de données avec Leaflet (JS/PHP)

  • 2 décembre 2014 à 15 h 29 min
    Permalink

    Bonjour,
    merci pour le tuto, ça fonctionne très bien. Comment faire ensuite pour récupérer les données (de la trace) dans la base et ensuite les afficher sur une carte avec leaflet ?
    bien cordialement

    Répondre
    • 5 décembre 2014 à 16 h 48 min
      Permalink

      Bonjour,
      Il y a plusieurs moyen d’y arriver. On peut transformer les données en geojson ou en WKT par exemple pour ensuite les lire avec Leaflet. Je vais essayer de publier un exemple dans un petit article la semaine prochaine.

      Répondre
      • 6 décembre 2014 à 13 h 59 min
        Permalink

        bonjour,
        merci … Mais il n’y a pas d’urgence. Autre question, j’aimerai aussi conserver dans la base de données l’élévation ainsi que le « temps » qui sont associées aux points de la trace gpx. Possible ou pas ?
        bien cordialement

        Répondre
        • 10 décembre 2014 à 13 h 49 min
          Permalink

          Bonjour,
          Il est possible d’avoir l’altitude de chaque points avec la fonction : get_elevation_data()
          https://github.com/mpetazzoni/leaflet-gpx
          Sinon, une fois le GPX converti avec la librairie ci-dessus, on peut accéder à d’autre données pour chaque point de la ligne. par exemple : ‘ma_feature[25]._latlng.meta.ele’ donnera l’altitude du 26ème poin. ‘ma_feature[25]._latlng.meta.time’ donnera le temps pour ce point.
          Après il est tout a fait possible de le stocker en base dans un nouveau champs séparé par un délimiteur. Dans le javascript, une fois spliter, l’index 25 correspondra au point qui à l’index 25.
          Bref, tout est possible, il faut définir les besoins et élaborer une stratégie qui correspond.

          J’ai publié un article pour afficher les traces que l’on avait intégré dans la base.

          Répondre
          • 11 décembre 2014 à 15 h 25 min
            Permalink

            bonjour,
            mille mercis pour ce nouvel article. Je me penche dessus pour pouvoir l’utiliser au plus tôt.
            cordialement Jacques

  • 4 novembre 2015 à 19 h 40 min
    Permalink

    Comment faire pour importer les waypoints SVP, merci

    Répondre
  • 4 novembre 2015 à 19 h 41 min
    Permalink

    Milles merci pour cet article mon frere, suis bloqué depuis bientot un mois.

    Répondre

Laisser un commentaire

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