Calculer un itinéraire grâce à Itinero et aux données OpenStreetMap

Cet article est la quatrième partie de ma série sur la cartographie libre :

Maintenant que nous savons afficher des cartes, on va essayer d'aller un peu plus loin ; aujourd'hui, nous allons calculer et afficher un itinéraire entre 2 points.

Le serveur de carte que nous avons installé précédemment ne va pas pouvoir nous aider, nous allons devoir utiliser un autre service pour ça. Il en existe plusieurs, j'ai décidé d'utiliser Itinero, qui est une librairie .NET, et qui propose aussi une api déployable directement.

Vous pouvez lire la documentation complète d'Itinero pour entrer plus en détail dans le sujet.

Création du fichier RouteDb

Nous avons déjà du effectuer une conversion des données OpenStreetMap vers un format vectoriel, le principe ici est le même, puisqu'Itinero ne va pas travailler non plus sur les données brutes, mais sur des données ne contenant que le nécessaires pour calculer un itinéraire (uniquement les routes, inutiles de s'encombrer des batiments ou des cours d'eau).

Les données OpenStreetMap peuvent toujours être récupérées par région depuis le site Geofabrik, et ensuite Itinero Data Processor permets d'effectuer la conversion en ligne de commande.

Tout ce que permets IDP est faisable par code grâce à un packge Nuget, et c'est ce que j'ai choisi de faire, afin d'automatiser complètement la récupération des données OSM, la conversion, et l'envoi du résultat vers un stockage cloud.

Pour cela, j'ai créé une application console, dans laquelle j'ai référencé les packages Nuget Itinero et Itinero.IO.Osm.
Puis quelques lignes de code pour récupérer les données de Midi-Pyrénées, les convertir, et sauvegarder le résultat.

var routerDb = new RouterDb();
var client = new HttpClient();

var url = "http://download.geofabrik.de/europe/france/midi-pyrenees-latest.osm.pbf";
using (var response = await client.GetAsync(url))
{
    routerDb.LoadOsmData(response.Content.ReadAsStream(), Vehicle.Car, Vehicle.Bicycle);
}

using (var stream = new FileInfo(@"midi-pyrenees.routerdb").Open(FileMode.Create))
{
    routerDb.Serialize(stream);
}

Deux choses importantes à voir dans ce code :

  • routerDb.LoadOsmData : cette méthode lis le fichier OpenStreetMap, et récupère les informations nécessaires au calcul d'itinéraire.
    Une liste de Vehicle est passé en paramètre, car selon le type d'itinéraire qu'on veut calculer, les données à utiliser ne seront pas les mêmes.
  • routerDb.Serialize : cette méthode permets d'enregistrer nos données dans un fichier RouterDb, ensuite nous n'utiliserons que ce fichier pour tous nos calculs d'itinéraires.

On a une liste de profils de véhicules prédéfinis (voiture, vélo, piéton ou camion…), et on peut définir d'autres profils personnalisés.

Vous pouvez lancer cette application, et la laisser tourner quelques minutes, ça sera plus ou moins long selon la taille de la zone à convertir, et le nombre de profils de véhicules que vous souhaitez gérer.

Calcul d'un itinéraire

Maintenant qu'on a un RouterDb, on va pouvoir l'utiliser pour calculer nos itinéraires.

On commence par charger les données depuis le fichier, puis on crée un Router utilisant ces données

RouterDb routerDb;
await using (Stream routerDbFile = await container.GetFile("midi-pyrenees.routerdb"))
{
    routerDb = RouterDb.Deserialize(routerDbFile);
}

var router = new Router(routerDb);

Il nous faut ensuite créer un Profile : avec quel type de véhicule on souhaite calculer l'itinéraire, et si on veut le plus rapide ou le plus court.

Là aussi, je reste sur les profils prédéfinis :

var profile = Vehicle.Car.Fastest();            // Trajet le plus rapide en voiture
var profile2 = Vehicle.SmallTruck.Shortest();   // Trajet le plus court en camionnette

Puis on choisit le point de départ et d'arrivée avec leurs coordonnées GPS, et on peut calculer l'itinéraire

var start = router.Resolve(profile, 43.60442f, 1.44403f);
var end = router.Resolve(profile, 43.23370f, 1.57595f);

var route = router.Calculate(profile, start, end);

L'objet Route contient toutes les informations qui nous intéressent, notamment :

  • Shape et ShapeMeta : les coordonnées de tous les points représentant la route, et les métadonnées associées
  • TotalDistance et TotalTime : la distance totale et la durée estimée du trajet

Ces informations suffisent maintenant à connaitre toutes les informations du trajet. Pour le dessiner sur une carte, on peut par exemple dessiner un trait entre chaque point du tableau Shape.

Cependant, plutôt que de faire ça manuellement, on préfèrera certainement utiliser le format GeoJson pour envoyer les données à une librairie compatible. Il suffit d'appeler la méthode ToGeoJson()

// Envoi du résultat depuis une API ASP.NET
return Ok(route.ToGeoJson());

Il suffit ensuite de récupérer les données en JavaScript et de les passer à la librairie MapLibre pour pouvoir l'afficher :

// On fait un appel à l'API pour recevoir les informations de l'itinéraire
const response = await fetch('/Itineraire');
const data = await response.json();

const map = initMap(); // voir les articles précédents pour l'initialisation de la carte

// On crée une source de données avec les informations geojson reçues
map.addSource('route', {
    type: 'geojson',
    data: data
});

// On affiche les données de la source avec l'id "route"
map.addLayer({
    'id': 'route',
    'type': 'line',
    'source': 'route',
    'layout': {
        'line-join': 'round',
        'line-cap': 'round'
    },
    'paint': {
        'line-color': '#888',
        'line-width': 8
    }
});

Démonstration

Voilà donc le résultat : j'ai repris l'affichage de la carte existant, et on peut afficher par dessus le trajet entre Toulouse et Saverdun.

Partager ses clés OpenPGP et trouver celles de vos correspondants

OpenPGP est un format de cryptographie utilisé pour le chiffrement des e-mails de bout en bout.
Le chiffrement avec OpenPGP est géré nativement par Thunderbird, et fonctionne avec n'importe quelle adresse e-mail. Des services comme Proton vous proposent un usage simplifié en embarquant directement son usage dans leur webmail.
Vous pouvez trouver une liste de logiciels compatibles sur le site OpenPGP.org

Le chiffrement repose sur une paire de clés (publique et privée) associée à chaque adresse e-mail : lorsque vous écrivez à quelqu'un, vous utilisez sa clé publique pour chiffrer le message, le destinataire utilisera sa clé privée pour le déchiffrer et lire son contenu.

S'il est bien sûr possible de partager sa clé publique en la donnant en main propre à ses correspondants, ce n'est pas forcément le plus pratique ; la question se pose ainsi de savoir comment partager sa clé publique par internet de manière sécurisée.

Partager sur un serveur de clés

Historiquement, on partageait ses clés sur un réseau de serveurs de clés décentralisés. Cette approche a trouvé ses limites, et aujourd'hui plusieurs projets OpenPGP se sont rassemblés pour créer et utiliser un serveur de clés principal de confiance : https://keys.openpgp.org/.

Ceci ne vous empêche en rien d'utiliser d'autres serveurs, mais en envoyant vos clés sur celui-là, votre adresse e-mail sera vérifiée (empêchant quiconque de fournir une fausse clé pour votre adresse), et les utilisateurs qui auront ce serveur configuré trouveront votre clé plus facilement.

Pour envoyer votre clé, allez sur la page "gérer votre clé" et entrez votre adresse e-mail. Vous allez recevoir un message avec un lien vous permettant d'envoyer vos clés ou de les supprimer du serveur.

Lorsque la clé est sur un serveur, elle est trouvable par l'API :
https://keys.openpgp.org/pks/lookup?op=get&options=mr&search=blog@lacasa.fr

Il est bien sûr toujours possible d'héberger son propre serveur de clés, et ça peut être pertinent si vous êtes en position d'autorité de confiance pour un grand nombre de clés. C'est par exemple ce qu'a fait Protonmail pour distribuer les clés de tous ses utilisateurs.

Cependant, il faut que les utilisateurs enregistrent manuellement votre serveur de clés dans leurs logiciels pour pouvoir en profiter, vous aurez probablement du mal à convaincre beaucoup de monde de le faire. C'est pour ça que nous avons un autre système de partage de clés : WKD

Partager directement avec Web Key Directory

Plutôt que de passer par un serveur externe en qui il faut avoir confiance, il est possible d'héberger ses propres clés.
L'idée est simple : si une adresse e-mail est en '@lacasa.fr', on doit pouvoir faire confiance au site web https://lacasa.fr pour fournir sa clé publique.

N'importe quel hébergement statique sera capable de fournir les clés, puisqu'il suffit de créer une arborescence et de donner le bon nom aux fichiers

https://[domaine]/.well-known/openpgpkey/hu/[Hashed-UserId]?l=[UserId]

Le nom du fichier est un hash du userId. Ainsi, pour l'adresse e-mail blog@lacasa.fr, le hash de blog est n4jfjtn9wusz7eehybkb96azsdg68ftf

La clé de cette adresse e-mail peut donc être publiée à cette adresse :
https://lacasa.fr/.well-known/openpgpkey/hu/n4jfjtn9wusz7eehybkb96azsdg68ftf?l=blog

Certains serveurs WKD ont besoin d'avoir le userId passé en clair, on l'ajoute donc le paramètre si besoin : l=blog

Il existe un mode avancé pour proposer ses clés, quand pour des raisons pratiques on ne peut pas ou on ne souhaite pas héberger ses clés directement sous le domaine racine.
Ce mode avancé permets aussi de centraliser l'hébergement des clés de plusieurs domaines sur un seul serveur.

Pour ça, il faut créer un sous-domaine openpgpkey au domaine de l'adresse e-mail, et ajouter aussi un dossier avec le nom du domaine dans le dossier .well-known/openpgpkey

https://openpgpkey.[domaine]/.well-known/openpgpkey/[domaine]/hu/[Hashed-Userid]?l=[UserId]

La clé de mon adresse e-mail est donc aussi accessible à cette adresse :
https://openpgpkey.lacasa.fr/.well-known/openpgpkey/lacasa.fr/hu/n4jfjtn9wusz7eehybkb96azsdg68ftf?l=blog

Ainsi on peut héberger les clés en dehors de son site web principal, et héberger plusieurs domaines sur le même serveur en faisant pointer les différents sous-domaine sur ce site web avec un CNAME.

Le serveur de clés keys.openpgp.org propose ainsi un service WKD utilisable avec votre propre domaine :
Créez le CNAME qui pointe vers wkd.keys.openpgp.org, et vos clés déjà hébergées sur leur serveur deviennent accessible en WKD sur votre propre domaine.

.NET et les clés OpenPGP

Si je me suis plongé dans le partage de clés OpenPGP, c'était aussi pour pouvoir chiffrer des e-mails envoyés depuis mes applications métier. J'ai donc fait une librairie .NET standard Pericia.OpenPgp pour retrouver facilement des clés sur un serveur de clés ou par WKD, que vous pouvez ensuite utiliser avec BoucyCastle.

var key1 = openPgpSearch.SearchWebKeyDirectory(mailAddress);
var key2 = openPgpSearch.SearchHttpKeyServer(mailAddress);

Cette librairie propose aussi une extension à ASP.NET pour distribuer vos clés via WKD (aussi bien en mode direct qu'en mode avancé). Il vous suffit de mettre vos clés publiques dans un dossier, et elles seront distribuées automatiquement.

// Configure services
var keyDirectory = builder.Environment.ContentRootFileProvider.GetDirectoryContents("Keys");
builder.Services.AddWebKeyDirectory(keyDirectory);

// Configure app
app.UseWebKeyDirectory();

Générer ses propres tuiles vectorielles à partir des données OpenStreetMap

Cet article est la troisième partie de ma série sur la cartographie libre :

Dans l'article précédent je vous ai montré comment il était possible d'héberger son propre serveur de carte, et donc de ne plus être dépendant de services externes pour afficher des cartes sur un site web. À un détail près, puisque même si on peut être autonome avec le serveur, il fallait quand même passer par un service payant pour pouvoir récupérer et mettre à jour les données cartographiques. Nous allons voir maintenant comment faire ça nous même.

Pourquoi les données cartographiques sont-elles payantes ?

On peut légitimement se poser cette question : puisque les données cartographiques viennent d'OpenStreetMap, leur usage est censé être libre - dans ce cas, pourquoi le téléchargement des données pour le serveur de tuile est-il payant ?

La réponse est toute simple : parce que les données OpenStreetMap ne peuvent pas être utilisées directement, et nécessitent donc une phase de conversion.

Il est tout à fait possible d'héberger une copie du site OpenStreetMap, avec son serveur de tuiles «images», et donc d'utiliser directement les données existantes. J'ai déjà mentionné le site Switch2OSM si le sujet vous intéresse.

Mais le rendu de ces tuiles images se fait vieillot, et je préfère proposer un affichage vectoriel, qui n'a que des avantages, maintenant que c'est supporté correctement sur tous les appareils un peu récents : format plus léger, zooms et déplacements plus fluides.

Nous allons donc voir comment effectuer la conversion des données natives OpenStreetMap vers un format vectoriel.

Le format OpenMapTiles

Les équipes de MapTiler, le service qui propose de télécharger les données cartographiques, sont à l'origine du format de tuiles verctorielles OpenMapTiles, distribué sous licence libre et gratuit à utiliser.
C'est ce format que j'ai utilisé dans mes articles précédent lorsque je parlais de tuiles vectorielles.

Étant donné que le format est libre, nous avons donc pu avoir différentes initiatives pour proposer des serveurs compatibles, ainsi que des projets pour convertir les données OpenStreetMap vers ce format. Nous pouvons citer en vrac (liste non exhaustive) : Openmaptiles-tools, Tilerator, Tilemaker, ou Planetiler

C'est ce dernier projet que j'ai décidé d'utiliser et que je vais vous présenter

Génération d'un fichier mbtiles avec Planetiler

Après avoir essayé différents outils pour générer mes données cartographiques vectorielles, j'ai décidé d'utiliser Planetiler car il est le plus simple à utiliser : il suffit d'une ligne de commande pour obtenir un fichier de mbtiles, qu'il ne nous reste plus qu'à envoyer sur notre serveur.

Tout d'abord, il faut savoir quelle zone on veut convertir. Vous pouvez vous contenter d'une région ou d'un pays, mais si vous souhaitez la planète entière il vous faudra beaucoup de mémoire RAM, et quelques heures à attendre.

Les données par région sont fournies par le site Geofabrik, et seront téléchargées automatiquement par l'outil. De mon côté, je me contente de la carte de France (métropole + corse)

Pour lancer la conversion, 2 options.

Utilisation de l'outil Java :

wget https://github.com/onthegomap/planetiler/releases/latest/download/planetiler.jar
java -Xmx32g -jar planetiler.jar --download --area=france

Avec l'image Docker:

docker run -e JAVA_TOOL_OPTIONS="-Xmx32g" -v C:\data:/data ghcr.io/onthegomap/planetiler:latest --download --area=france

Ces lignes de commande viennent du REAMDE de l'outil, que j'ai très légèrement adaptées :

  • l'option Java -Xmx32g représente la mémoire utilisable par l'outil. Elle est limitée à 1Go dans leur exemple, suffisant pour la principauté de Monaco, mais limite pour la France
  • et j'ai remplacé la variable shell $(pwd) par un chemin en dur, car je le fait tourner sous Windows et que la syntaxe Powershell n'est pas exactement la même (j'avoue que je n'ai pas cherché comment l'écrire plus proprement)

Après trois quarts d'heure à mouliner sur mon PC, j'ai enfin obtenu ma carte, que j'ai pu mettre sur mon serveur de tuiles.

Si vous souhaitez convertir la planète entière, il vous faudra idéalement plus de 100Go de RAM, et de nombreux cœurs dans votre processeur. Vous pouvez lire une documentation spécifique dans laquelle je ne rentrerai pas en détail.
Personnellement, je n'ai même pas essayé de lancer ça sur mon PC, il faudra certainement préférer utiliser des machines virtuelles cloud pour ça. Pour exemple, la conversion complète de la planète a été effectuée en un peu plus de 3h sur une machine avec 16 cœurs et 128Go de ram.

Avec ce 3ème article, vous avez maintenant tout le nécessaire pour héberger vos cartes et les mettre à jour régulièrement, et j'espère vous avoir convaincu de le faire !