TL;DR : carte OpenStreetMap des câbles sous-marins d'Internet / Internet submarine cables map OpenStreetMap. Ci-dessous, pourquoi et comment construire une telle carte.
ÉDIT DU 29/08/2023 : depuis le 15/11/2022, TeleGeography a supprimé son dépôt git et ne diffuse plus les données géographiques (« We no longer maintain and update a repository on GitHub for our data or our source code » dans l'infobulle « about » sur leur site web). :( Cette carte n'est donc plus à jour. Dernière version des données géographiques. FIN DE L'ÉDIT.
Loin d'être virtuel, Internet repose sur des infrastructures physiques dont des câbles sous-marins en fibre optique (qu'il faut entretenir contre l'usure, les événements naturels ou humains, les sabotages, etc.).
La liste de ces câbles, leurs stations d'atterrissement et leur tracé est environ public (environ car le tracé ne l'est pas de manière précise). Nous disposons de l'excellent site web Submarine Cable Map.
Néanmoins, cet existant, cette carte, me pose deux problèmes :
Pour l'heure, les données (le tracé des câbles et les coordonnées géographiques des stations d'atterrissement) de la carte « Submarine Cable Map » sont disponibles dans un dépôt git public sous une licence environ libre (CC BY-NC-SA).
Et si on les utilisait pour construire une carte basée sur OpenStreetMap, l'outil de cartographie libre et communautaire (OSM) ?
C'est chose faite : carte OpenStreetMap des câbles sous-marins d'Internet / Internet submarine cables map OpenStreetMap.
J'actualiserai cette carte au fil de l'eau, en me basant sur le dépôt git sus-cité.
Ci-dessous, un retour sur la façon dont j'ai procédé pour construire cette carte.
Pour réaliser cette carte, il faut deux choses.
D'un côté, il faut pouvoir créer une carte personnalisée en utilisant un fond de carte OpenStreetMap, c'est-à-dire une carte sur laquelle on peut représenter ce que l'on veut : un itinéraire, un parcours de trail, un marqueur sur un lieu avec une éventuelle infobulle pour donner des infos, plusieurs marqueurs afin d'identifier plusieurs lieux (pour un festival décentralisé, par exemple), etc.
Le site web officiel d'OpenStreetMap permet de tracer des itinéraires et de partager une carte avec un marqueur sur un lieu (sans infobulle, sans marqueur personnalisé), mais ça s'arrête là.
Le logiciel web umap pemet tout le reste sus-cité. On peut installer sa propre instance ou utiliser celles qui existent. Exemples : instance umap OSM France, instance umap Framasoft.
Vu mon maigre besoin, j'ai décidé d'utiliser une instance existante. J'ai choisi celle de Framasoft, une association française qui œuvre pour des services numériques éthiques et la culture libre.
Pour débuter, je recommande vivement cet exellent tutoriel umap. D'autres tutoriels, et même des tutoriels vidéos sont disponibles sur le wiki officiel OSM France.
D'un autre côté, il faut retravailler les données géographiques disponibles :
On pourrait faire ça à la main, mais de nouveaux câbles entrent régulièrement en service pendant que d'autres rendent leur tablier. Une mise à jour à la main ne serait pas gérable. Il faut donc qu'un programme informatique fasse cette transformation des fichiers d'origine prévus pour Google Maps en fichiers compréhensibles par OpenStreetMap en y ajoutant, au passage, nos desiderata de mise en forme.
Dès que l'on évoque la manipulation de JSON, on pense forcément à la commande jq
. J'ai écrit un tutoriel pour jq qui est rien de plus que le retour de l'expérience acquise pour construire cette carte des câbles sous-marins d'Internet.
Voici le script que j'ai pondu : transform_carte_cables_sous-marins.sh.
git
et jq
doivent être installés ;Une fois que l'on a exécuté ce script afin de traiter les données, comment créer la carte et y importer les données ?
Créer une carte sur une instance umap ;
Dans l'entête, à côté de « Carte sans nom », il y a une icône en forme de crayon. Cliquer dessus :
Limites géographiques (pour limiter les déplacements sur la carte aux endroits où il y a des câbles afin d'éviter les fausses manips) :
Cliquer sur le bouton « Enregistrer ». Garder impérativement le lien de modification (tant que le cookie est conservé par ton navigateur web, ça va, mais sinon il constitue le seul moyen de pouvoir modifier la carte) ;
Cliquer sur le bouton « calques » :
Supprimer le calque 1 ;
Ajouter un calque :
Description = « Landing stations » ;
Propriétés de la forme :
Ajouter un calque :
Description = « submarine cables » ;
Propriétés de la forme :
Cliquer sur le bouton « Enregistrer » ;
Cliquer sur le bouton « importer des données » :
Je note quelques limites d'umap :
#<niveau_de_zoom>/<latitude>/<longitude>
). Dit autrement : je n'ai pas trouvé un moyen de faire un lien vers un objet. Sur OSM, les objets (le tracé d'une ligne de bus, par exemple) ont un identifiant interne public, donc on peut construire une URL de la forme « /relation/<identifiant>
». Avec umap, je n'ai pas trouvé d'identifiant, y compris dans l'export ;Le script présenté plus haut permettant de traiter les données géographiques avant importation dans umap n'est pas le premier que j'ai écrit. Le premier, c'est celui-ci : transform_carte_cables_sous-marins.original.sh.
Quelles différences ?
jq
peut mettre le contenu d'un (ou plusieurs) fichier secondaire dans une (ou des) variable afin d'enrichir le fichier principal avec des informations du (des) fichier secondaire ;jq
. Tout filtre ajouté à la chaîne sera exécuté autant de fois qu'il y a d'éléments dans le tableau parcouru. Cela signifie aussi que l'on perd tout le contenu JSON qui était autour de ce tableau. Oui, c'est aussi idiot que de se plaindre qu'avec un grep | xargs <maCommande>
, maCommande n'accède pas à ce qui n'a pas été retenu par grep
. Mais, du coup, ça me fait perdre l'entête et le pied du fichier JSON d'origine, donc ça produit un JSON invalide. Si je n'itère pas sur le tableau, je ne peux pas accéder aux attributs de chaque élément du tableau, donc il m'est impossible, par exemple, de transvaser la couleur d'un câble dans un attribut compréhensible par umap (genre jq '.features[].properties += { "_umap_options": { "color": .color } }' cables.json
, ça ne fonctionne pas). Comme je n'avais pas compris que jq
permet de travailler sur plusieurs fichiers, je n'avais pas perçu que l'on peut reconstruire le fichier JSON d'origine ;Ces trois points sont détaillés dans mon shaarli dédié à la prise en main de jq par des exemples pratiques.
En conséquence, je faisais une boucle « for » avec mon shell. La fin de l'itération était le nombre d'éléments dans le tableau - 1. Ainsi, j'avais un indice de tableau. Donc je pouvais accéder à l'élément que je voulais dans le tableau, récupérer son identifiant (permettant de récupérer ses infos dans le fichier JSON séparé) et le modifier.
jq
(récupérer l'identifiant d'un élément, récupérer les infos dans le deuxième fichier, modifier le JSON avec tout ça) ‒ ce qui implique des fork() et des exec() en pagaille ‒, que j'écrivais autant de fichiers temporaires qu'il y a d'éléments parcourus, et que jq renvoi l'intégralité du JSON à chaque passe. Tout ça consomme du CPU, de la RAM et des IO. Ce premier jet s'exécute en 1 minute 54 (moyenne sur 3 exécutions, récupération du dépôt git exclue). Et, encore, je suis en RAM (tmpfs).jq
distincte, je peux appliquer très facilement un grep -v 'null'
afin de dégager de la sortie les attributs JSON non-remplis (genre un câble qui n'a pas de site web associé, par exemple). EDIT DU 21/08/2021 : pour afficher un texte pré-défini quand la valeur d'un attribut JSON est null, jq dispose d'un opérateur de remplacement, « // ». FIN DE L'ÉDIT.printf()
genre « Working on cables: 001/469… […] 142/469… ». Ça fait bien longtemps que je n'avais pas écrit un code générant un affichage aussi kikoo. :DLa version actuelle du script utilise une seule commande jq
(mais avec des filtres supplémentaires) afin de réaliser l'itération, l'enrichissement du JSON et la modification du flux JSON.
jq
commence à devenir touffue et très compliquée à comprendre, donc à maintenir dans le temps. Oui, je pourrais virer des variables, mais guère plus, je pense.jq
mais au prix de la lisibilité. EDIT DU 21/08/2021 : pour afficher un texte pré-défini quand la valeur d'un attribut JSON est null, jq dispose d'un opérateur de remplacement, « // ». FIN DE L'ÉDIT.
Merci Johndescs pour la relecture et la correction des scripts.