5505 links
  • GuiGui's Show

  • Home
  • Login
  • RSS Feed
  • Tag cloud
  • Picture wall
  • Daily
Links per page: 20 50 100
page 1 / 1
  • Construire une carte des câbles sous-marins d'Internet avec OpenStreetMap

    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.


    Introduction

    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 :

    • Elle repose sur le service Maps de la multinationale Google. Ça pose les habituelles questions de dépendance vis-à-vis d'un acteur unique hors contrat, de pérennité et de respect de la vie privée ;

    • Elle est plutôt unique, donc elle peut disparaître promptement. À titre d'exemple, les repreneurs du site web historique en la matière, cablemap.info, demandent une inscription préalable, donc, en pratique, cette carte publique a disparu. J'ai vu que les sources du site web sont disponibles (donc que quiconque peut en héberger une copie), mais node.js, yarn et compagnie, ça sera sans moi (et ça ne résout pas la dépendance à Google Maps).

    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.


    Que faut-il pour créer 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 :

    • Les tracés des câbles sont dans un fichier JSON alors que les informations sur les câbles (nom, longueur, date de mise en service, etc.) sont stockées dans un autre fichier ;

    • On aimerait mettre en forme (gras, liste à puces, liens vers les stations d'atterrissement, etc.) le texte associé à un câble / une station ;

    • On aimerait conserver la coloration des câbles, mais l'attribut JSON utilisé n'est pas prise en charge par umap (et sa valeur n'est pas au format attendu ‒ # ‒).

    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.

    • Les logiciels git et jq doivent être installés ;

    • Il suffit de le lancer. Aucun argument attendu ;

    • Il va récupérer le dépôt git, créer un espace de travail temporaire, travailler, détruire l'espace de travail temporaire, indiquer où trouver les fichiers résultants de son traitement ;

    • Deux variables en début de script permettent d'ajouter des paramètres umap sur les câbles et les stations (opacité, forme, interaction, etc.). Pour l'instant, tous les paramètres peuvent être définis au niveau du calque, donc ces variables ne servent pas.


    Construire la carte

    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 :

      • Nom = « Submarine Cable Map » ;

      • Description = contenu du fichier summary.txt ;

      • Options d'interface :
        • Afficher le bouton de plein écran = jamais ;

        • Afficher le bouton de localisation = jamais ;

        • Afficher le bouton pour ouvrir l'éditeur d'OpenStreetMap = jamais (je ne souhaite pas donner de faux espoirs au pékin : ce ne sont pas les données relatives aux câbles qui seront modifiables par ce biais) ;

        • Afficher le bouton d'accès rapide aux couches de données = caché ;

        • Voulez-vous afficher un panneau latéral au chargement? = légende.
      • 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) :

        • Sud = -90 ;

        • Ouest = -250 ;

        • Nord = 90 ;

        • Est = 250.
      • Crédits
        • Crédit = « CC BY-NC-SA 3.0 \n Original (Google Maps): [[https://www.submarinecablemap.com/|Submarine Cable Map]] by [[https://www2.telegeography.com/|TeleGeography]]. \n Data: [[https://github.com/telegeography/www.submarinecablemap.com|Github TeleGeography]]. ».
    • 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 :

        • Nom = « landings » ;

        • Description = « Landing stations » ;

        • Propriétés de la forme :

          • Forme de l'icône = cercle (elle attire moins l'œil / monopolise moins l'espace disponible).
        • Options d'interaction :
          • Forme de popup = panneau latéral (plus pratique que les infobulles + comportement par défaut d'OSM).
      • Ajouter un calque :

        • Nom = « cables » ;

        • Description = « submarine cables » ;

        • Propriétés de la forme :

          • Opacité = 1 (curseur le plus à droite possible).
        • Options d'interaction :
          • Forme de popup = panneau latéral.
    • Cliquer sur le bouton « Enregistrer » ;

    • Cliquer sur le bouton « importer des données » :

      • Importer des données = choisir le fichier landings.json produit par le script sus-mentionné ;

      • Choisir le calque de données pour l'import = landings ;

      • Cliquer sur le bouton « Importer ».
    • Cliquer sur le bouton « Enregistrer » ;

    • Cliquer sur le bouton « importer des données » :
      • Importer des données = choisir le fichier cables.json produit par le script sus-mentionné ;

      • Choisir le calque de données pour l'import = cables ;

      • Cliquer sur le bouton « Importer ».
    • Cliquer sur le bouton « Enregistrer » ;

    • À la fin de l'URL, ajouter « #2/-0.2/0.0 » et valider ;

    • Cliquer sur le bouton « enregistrer le zoom et le centre actuels » ;

    • Cliquer sur le bouton « exporter et partager la carte » :
      • Télécharger les données = données complètes de la carte ;

      • Cliquer sur le bouton « télécharger les données ». Conserver ce fichier de sauvegarde.



    Je note quelques limites d'umap :

    • Contrairement à la version originale de la carte, je ne suis pas parvenu à créer un lien vers un câble sous-marin dans le panneau latéral d'une station d'atterrissement. De même, pour avoir un lien vers une station d'atterrissement dans le panneau latéral d'un câble, j'ai dû ruser en utilisant ses coordonnées géographiques plutôt que son nom afin de former une URL valide au sens OSM (#<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 ;

    • Contrairement à la version originale de la carte, je n'ai pas trouvé comment mettre en évidence / surligner l'ensemble du tracé d'un câble quand on clique dessus, puisque, comme indiqué au point précédent, je n'ai pas trouvé comment pointer un objet par son nom afin de créer un lien pointant sur lui dans son intégralité.


    Annexe : premier script, méconnaissance de jq, optimisation et autocritique

    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 ?

    • Je n'avais pas compris que 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 ;

    • Je ne voulais pas admettre qu'une fois qu'on a sélectionné / demander l'affichage d'un attribut qui contient un tableau (« '.monAttribut[]' »), alors on itère sur ce tableau jusqu'à la fin de la commande 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 ;

    • Comme je n'avais pas compris non plus que l'on peut circonscrire une itération sur un tableau au sein d'une opération bornée / finie comme une affectation, je n'avais pas perçu que ça pouvait être une autre solution au problème du point précédent. C'est finalement la solution que j'ai retenue.

    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.

    • L'inconvénient, c'est que je lançais plusieurs 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).

    • L'avantage de cette première version, c'est que, puisque je récupère les informations (sur un câble ou une station) avec une commande 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.

    • Autre avantage : pour faire passer le temps d'exécution, j'affiche un joli compteur avec 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. :D

    La 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.

    • Cette dernière version s'exécute en 3,6 secondes (conditions de test identiques). 31 fois plus rapide ! :O

    • Inconvénient : la commande 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.

    • Le premier avantage de la première version devient un inconvénient : je ne peux plus virer aussi facilement les attributs JSON non-remplis. Probablement que ça peut se faire avec une condition 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.

    Sun Apr 5 21:15:14 2020 - permalink -
    - http://shaarli.guiguishow.info/?UJyNPw
Links per page: 20 50 100
page 1 / 1
Mentions légales identiques à celles de mon blog | CC BY-SA 3.0

Shaarli - The personal, minimalist, super-fast, database free, bookmarking service by the Shaarli community