Greboca  

Suport technique et veille technologique

Aujourd’hui, les grandes entreprises et administrations publiques hésitent entre continuer à utiliser des logiciels propriétaires ou basculer vers les Logiciels Libres. Pourtant, la plupart des logiciels libres sont capables de bien traiter les données issues des logiciels propriétaire, et parfois avec une meilleur compatibilité.

C’est alors la barrière de la prise en main qui fait peur, et pourtant...

Les logiciels libres

L’aspect « Logiciel Libre » permet une évolution rapide et une plus grande participation des utilisateurs. Les aides et tutoriels foisonnent sur Internet ou sont directement inclus dans le logiciel lui-même.

Enfin, les concepteurs sont plus proches des utilisateurs, ce qui rend les logiciels libres plus agréable à utiliser et conviviaux.

Grâce à la disponibilité des logiciels libres, vous trouverez facilement des services de support techniques et la licence n’est plus un frein à l’utilisation de ces logiciels par votre personnel.

Notre support technique concerne essentiellement les logiciels libres, que ce soit sous forme de services ponctuels ou de tutoriels.

LinuxFr.org : les journaux  -  Openstreetmap et GPS garmin

 -  Juin 2022 - 

Sommaire

Hello

En passant, je vous présente un petit projet sur lequel je bosse quand j'ai un peu de temps. Bonne lecture.

Contexte

En 2013, une moule avait posté un chouette post sur l'utilisation d'un montre garmin sous linux : garmin-forerunner-110.

Comme je suis un peu un mouton qui adore réinventer la roue, je me suis dis que j'allais faire pareil, avec une montre similaire : la garmin forerunner 10.

Pour faire court, quand on va courir, la montre enregistre un fichier .FIT récupérable simplement par connexion USB (le système de fichier monté est du FAT32).

Objectif

Avec ce projet, je voulais un truc simple: une liste de sorties sur lesquelles je peux cliquer, et l'affichage de la course sélectionnée sur un fond de carte.

Comme souvent, sur ce genre de projet, je tente d'apprendre des trucs, comme ça fait longtemps que je commencé, j'ai eu le temps de tester plein de trucs :

  • awk
  • bash
  • python
  • automake
  • gtk
  • openstreetmap

Mais j'y reviendrai, peut être.

Après avoir cherché et testé différents concepts, je me suis arrété sur le suivant, à savoir utiliser leaflet.js qui permet d'afficher simplement des données json sur une carte openstreetmap

Pour cela, il faut

  • convertir certaines informations des enregistrements en json (yaka)
  • afficher le tout sur le fond de carte (fokon)

Fokon : utiliser OSM à la maison

Pour gribouiller sur des cartes, on utilise donc leaflet.js.

C'est pas trop compliqué, tout tiens dans un fichier html (dont beaucoup de javascript) :

  • On crée un div qui contiendra la carte, on l'appelle 'map' (on force sa taille à 100%)
  • On crée une carte L.map qu'on place dans le div précédent
  • On crée un chemin L.geoJSON qu'on place sur la carte. Le format geojson est documenté là : [geojson.org].

Un petit truc qui m'a géné : les paramètres de latitude et longitude semblent inversé entre L.map et L.geoJSON, dans le premier, on donne latitude, longitude, dans le second, longitude, latitude…

Voici donc un petit fichier HTML qui fait ça, on notera que j'ai mis en local les fichier leaflet.[js,css] pour simplifier les tests, mais on peut les remplacer par un cdn.

</span>
<html lang="en">
<head>
  <meta charset="utf-8"/>
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"/>
  <script src="./leaflet.js">script>
  <link rel="stylesheet" href="./leaflet.css" />
<style> 
html, body { height: 100%; padding: 0; margin: 0; }
#map { width: 100%; height: 100%; }
style>
head>
<body onload="init()">
<div id="map">div>
<script type="text/javascript">

// fonction appelée au démarrage
function init() {
    // deux variables, pour contenir la carte et le chemin qu'on trace
    var map;
    var path;

    // la petite ligne de copyright
    var szAttr = '&copy; OpenStreetMap contributors<\/a>';

    // On choisi les tuiles osm par défaut
    var layer_osm = L.tileLayer(
        'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
        attribution: szAttr
    });

    // Création de la carte avec les attributs qui vont bien:
    // * dans le div "map"
    // * ou on regarde
    // * zoom
    // * tuiles à utiliser
    map = L.map('map', {
        center: [-49.35, 70.3],
        zoom: 13,
        layers: [layer_osm]
    });

    // Création du chemin proprement dit
    path = L.geoJSON([{
                "type": "LineString", 
                "coordinates": [[70.265, -49.354], [70.25,-49.354], [70.25,-49.35], [70.265, -49.35], [70.265, -49.354]]
    }]);

    // Et on affiche le chemin sur la carte
    path.addTo(map);

    console.log('done');
}

script>
body>
html>

Si le fichier est ouvert sur un navigateur, il devrait faire un petit rectangle autour d'une station sur une petite ile australe…

Tadaa

Taadaa !

On sait maintenant à quoi doit ressembler notre yaka : quelque chose qui transforme les .FIT en geojson.

Yaka : de fit à geojson.

Ici, on a deux solutions, on peut utiliser fitdump et décoder le contenu avec awk (comme dans le premier journal). L'inconvénient de cette solution et qu'il faut l'adapter à chaque montre : le code des messages change en fonction des paramètres que peut stocquer la montre. La modification n'est pas énorme, c'est simplement le code du Local message type qui est à revoir, mais bon…

On on peut s'amuser à faire une bibliotèque pour travailler avec les fichiers .FIT et un programme que l'utilise pour générer du json.

Le format .FIT est bien décrit par garmin, leur SDK propose pas mal de choses là dessus.
Un .FIT, c'est une succession d'enregistrements. On en a de deux types:

  • Les définitions,
  • Les données.

Les définitions décrivent comment sont organisées les données, elles contiennent :

  • le type de données (parmis un choix donné)
  • les champs contenus dans l'enregistrement.

Ok, comme ça c'est pas clair… Disons que dans un fichier, on peut avoir un message disant :

"À partir de maintenant, les données de type FIT_GLOBAL_RECORD contiennent un champ latitude et un champ longitude"

Les données sont variables, on sait ce qu'elle contiennent grace aux définitions.

libfit: lire les .FIT

Implémenter une specification, c'est pas très marrant à décrire, je vous passe donc les détails, mais on peut tout trouver ici: libfit

C'est une petite bibliothèque qui propose quelques fonctions qui font ce qu'on veut:

  • FIT_open: ouvre le fichier .FIT (en vérifiant au passage sa cohérence)
  • FIT_readRecord: lit le prochain enregistrement disponible
  • FIT_decodeValues: décode les valeurs contenues dans un enregistrement.

Et générer du geojson

Couplée à libjson-c, on a tout ce qu'il faut pour créer un convertisseur qui tient en 64 lignes:

#include 
#include 
#include "libfit.h"
#include "libfit-messages.h"

#define die(...) do { fprintf(stderr, __VA_ARGS__); exit(1); } while(0)

int main(int argc, char *argv[]) {
    if (argc != 3) {
        die("usage %s  ", *argv);
    }

    /* open the fit file */
    fit_file_t *f;
    fit_record_t *rec = NULL;
    f = FIT_open(argv[1]);
    if (NULL == f)
        die("cannot dump %s\n", argv[1]);

    /* prepare the json file */
    struct json_object *obj, *path, *coordinates;
    obj = json_object_new_object();
    path = json_object_new_object();
    json_object_object_add(path, "type", json_object_new_string("LineString"));
    coordinates = json_object_new_array();

    /* convert */
    for (;;) {
        double lat, lon;
        /* read next record */
        fit_record_t *rc = FIT_readRecord(f, rec);
        if (!rc) {
            FIT_free(rec);
            break;
        }
        rec = rc;
        /* only kee global record */
        if (FIT_GLOBAL_RECORD != FIT_getRecordType(rec))
            continue;

        /* decode position and speed */
        if (0 == FIT_decodeValues(rec, FIT_GLOBAL_RECORD,
                FIT_RECORD_POSITION_LONG_S32, &lon,
                FIT_RECORD_POSITION_LAT_S32, &lat,
                -1)) {

            struct json_object * xy = json_object_new_array();
            json_object_array_add(xy, json_object_new_double(lon));
            json_object_array_add(xy, json_object_new_double(lat));
            json_object_array_add(coordinates, xy);
        }
    }

    FIT_close(f);

    /* finalize */
    json_object_object_add(path, "coordinates", coordinates);
    json_object_object_add(obj, "path", path);

    /* save json file */
    json_object_to_file(argv[2], obj);
}

Un peu de mise en forme

On se retrouve avec un fichier json de 11 ko (pour un fit de 8), pas génial de mettre ça directement dans le fichier index.html. Il est assez facile de créer une fonction qui récupère le contenu d'un json via fetch, mais le problème de cette méthode est qu'elle impose la mise en place d'un serveur http, car firefox interdit les COR, comme j'ai envie de rester simple pour cette note, on va s'en passer. (Notez que c'est facile de mettre en place un serveur qui fait le job: python -m http.server suffit)

On commence par transformer notre json en js. Sachant que le fichier json tient sur une seule ligne, on peut se contenter d'inserer un peu de code en tête:

sed 's/^/var data = /' test.json > test.js

Voilà, on peut alors charger notre fichier via une balise <script></code></p>

<pre><code class="html"> <span class="p"><</span><span class="nt">script</span> <span class="na">src</span><span class="o">=</span><span class="s">"./test.js"</span><span class="p">></</span><span class="nt">script</span><span class="p">></span></code></pre>

<p>Au lieu de donner des coordonnées en dur, cette nouvelle variable est utilisée pour centrer le plan et dessiner le chemin :</p>

<pre><code class="html"> map = L.map('map', {
center: [data.path.coordinates[0][1], data.path.coordinates[0][0]],
zoom: 13,
layers: [layer_osm]
});

path = L.geoJSON(data.path),</code></pre>
<h4 id="toc-et-voilà">Et voilà</h4>

<p><img src="//img.linuxfr.org/img/68747470733a2f2f6672616d616769742e6f72672f6d6162752f6c69626669742f2d2f7261772f6d61737465722f64656d6f2f322f322e706e673f696e6c696e653d66616c7365/2.png?inline=false" alt="Taadaa !" title="Source : https://framagit.org/mabu/libfit/-/raw/master/demo/2/2.png?inline=false"></p>
<h3 id="toc-les-sources">Les sources</h3>

<p>Toutes les sources ici : <a href="https://framagit.org/mabu/libfit">libfit</a></p>
<h4 id="toc-et-après">Et après ?</h4>

<p>Dans un autre <a href="https://framagit.org/mabu/BasicFitConvert">repo</a>, j'ai codé un truc plus complet, dans lesquels on retrouve :</p>

<ul>
<li>Le calcul de la hauteur à partir de la position GPS (merci <a href="https://pypi.org/project/elevation/">elevation</a>)</li>
<li>L'affichage de la vitesse à l'aide de <a href="https://www.chartjs.org/">Chart.js</a>
</li>
<li>Afficher plusieurs trace en même temps, pour faire comme une heatmap…</li>
</ul>

<p>P'tet qu'un jour, je documenterai ça</p>
<div><a href="https://linuxfr.org/users/purplepsycho/journaux/openstreetmap-et-gps-garmin.epub">Télécharger ce contenu au format EPUB</a></div> <p>
<strong>Commentaires :</strong>
<a href="//linuxfr.org/nodes/127987/comments.atom">voir le flux Atom</a>
<a href="https://linuxfr.org/users/purplepsycho/journaux/openstreetmap-et-gps-garmin#comments">ouvrir dans le navigateur</a>
</p>

par purplepsycho

LinuxFr.org : les journaux

LinuxFr.org : Journaux

firefox, nouvelle fenêtre dans une session isolée

 -  15 avril - 

Les fenêtres de navigation privées de firefox partagent leurs cookies de session or je souhaitais avoir des fenêtres de navigation isolées, (qui ne (...)


Pretendo tente de déprogrammer l'obsolescence des consoles Nintendo

 -  9 avril - 

Ah Nal,Gros N vient de faire un gros doigt aux utilisateurs de ses consoles 3DS et Wii U en annonçant la fermeture des services en ligne pour (...)


[Trolldi] Vulgarisation sur l'IA pour décideur pressé

 -  5 avril - 

Cher 'Nal,Je fais un article-marque-page sur un post tout frais de Ploum où il est question d'un fantasme vieux comme le Talmud avec le Golem. (...)


Super Marian and Robin: les roms en collant

 -  3 avril - 

Bonjour Nal,Je t'écris pour te proposer de tester mon nouveau jeu: Super Marian and Robin.Il s'agit d'un jeu de plateformes pour un ou deux (...)


Le roi est mort, vive le roi ! Les alternatives de Redis sont là

 -  3 avril - 

Bonjour Nal !Après le changement de licence de Redis, ce qui devait arriver arriva, et des alternatives libres apparaissent.Tout d'abord, on a (...)