Introduction
Etat des lieux de l’exploitation des cartes géographiques dynamiques sur interfaces graphiques
Le couplage entre la puissance de calcul des systèmes embarquées et leurs systèmes de géolocalisation donne aujourd’hui naissance à de nombreux services basés sur l’utilisation de données géographiques. Ces données sont le plus souvent exploitées sous forme de cartes interactives.
La plus connue des applications utilisant ce type de cartes est bien entendu Google Maps. De nombreux sites internet utilisaient jusqu'à aujourd’hui ce serveur de cartes pour afficher des informations géographiques correspondant à leurs services.
A partir de 2012, ce service est devenu payant pour les sites à fort trafic (supérieur 25000 requêtes par jour). Une nouvelle révision tarifaire a eu lieu à partir du 11 juin 2018 réduisant le nombre de requêtes gratuite à 28000 par MOIS. Les sommes à payer pour garder le service de cartographie de Google en place sont plutôt rédhibitoires (environ 6€ pour chaque millier de chargements supplémentaires).
A titre d'exemple, un site qui était à la limite gratuite de chargement de 25000 cartes par jour passe d'une tarification de 0€ à 4068€ par mois. De nombreux services basées sur Google Map voient leur business model s'effondrer suite à cette flambée des coûts d'exploitation et cherche aujourd'hui une solution alternative à Google Maps.
L’objectif ici n’est pas de remettre en cause le modèle économique de Google, mais plutôt nous rendre compte des risques économiques et éthiques liés à notre dépendance par rapport aux Bases De Données (BDD) privées.
Dans le cadre de la BDD géographique, il existe une belle et solide alternative à Google Maps. Elle se nomme Open Street Map (OSM).
Open Street Map est un projet international collaboratif fondé en 2004 ayant pour but de créer une carte libre du monde. Ce projet collecte les données du monde entier sur les routes, les voies ferrées, les rivières, les forêts, les bâtiments et bien plus encore, pour ainsi constituer une carte du monde libre et accessible par chacun. Les données cartographiques collectées sont ré-utilisables sous licence libre ODbL (Open Database Licence) depuis le 12 septembre 2012.
Je vous laisse juger de la qualité de cette base de données et de son rendu (lien vers Open Street Map).
Le côté collaboratif vient du fait que vous pouvez également devenir contributeur à l’enrichissement de cette base de données géographique en identifiant les éléments d’une carte satellitaire connus et/ou reconnaissables. Il suffit juste de vous enregistrer et de suivre un petit tutoriel qui décrit les bonnes manipulations à effectuer pour pouvoir enrichir la BDD et c'est parti !
Les outils et services se basant sur OSM se sont considérablement développés ces dernières années, et les logiciels Open Source se font ici la part belle à l’exploitation de cette source dite d"Open Data".
Architectures pour l'exploitation des données géographiques
L'exploitation de la base de donnée géographique d'OSM peut se faire de différentes façon. Elles sont représentées sur le schéma ci-dessous :
Parmi ces chemins, il en existe un que nous ne pouvons pas utiliser avec OSM. Il concerne l’exploitation directe des cartes à partir du serveur openstreetmap.org, comme nous pourrions le faire au travers de l’application Google Maps sur notre téléphone portable. Contrairement à Google, la fondation OpenStreetMap est une organisation à but non lucratif et possède des moyens limités. Les serveurs d’OSM n’ont tout simplement pas la capacité de répondre à un nombre de requêtes élevées (cf Politique d'usage des tuiles).
Malgré cette restriction, il nous reste tout de même de nombreuses méthodes pour exploiter la richesse de cette base de donnée.
Obtention de cartes dynamiques à partir de tuiles
La méthode de visualisation de carte dynamique à partir de tuile est appelée TMS (Tile Map Service). Elle permet la visualisation et le déplacement de carte sur un écran avec différents niveaux de zoom. Cette méthode permet d'adapter l’information à afficher en fonction du zoom et d'obtenir une résolution optimale.
Cette méthode est basée sur la concaténation d’images appelées "tuiles", de taille fixe de 256 x 256 pixels au format png. Une tuile affichée au niveau n correspondra à 4 tuiles affichés au niveau n+1. Avec 18 niveaux de zoom, le nombre total de tuiles à générer pour afficher l’ensemble de la map monde sera de plus de 68 milliards de tuiles (4^18).
L’ensemble des tuiles est obtenu par combinaison des données géographiques et des feuilles de styles grâce une opération de rendu. Les feuilles de style transforment l’information contenue dans la base de donnée en objet graphique et à partir de règles liées :
- à la nature de l’information géographiques
- au niveau de zoom (de manière à limiter la surcharge d’informations des cartes sur des faibles niveaux de zoom)
Je vous invite donc pour la suite de ce tutoriel à vous limiter à une zone géographique très réduite. Le temps de rendu et la taille des dossiers de tuiles peuvent rapidement devenir très élevée.
Création et exploitation de nos cartes "offline"
Notre objectif est d’héberger la carte et son outil de visualisation au sein d’un système embarqué. Nous sommes ici en mode "hors ligne" et le schéma de production/exploitation des cartes (issu du schéma précédent) est le suivant :
Création de nos propres tuiles
Le chemin que nous allons suivre pour concevoir nos propres tuiles est le suivant :
- Téléchargement des données géographiques complètes ou partielles issues de la base d'OSM
- Intégration de ces données dans une base de données spatiale locale nommée postgresSQL/ postGIS via l’outil osm2pgsl
- Transformation de BDD locale en tuiles grâce à l'outil Mapnik
Nous avons utilisé la distribution UBUNTU 16.04 LTS sur un ordinateur standard pour produire notre carte et le système d'exploitation Raspbian sur Raspberry Pi pour la visualiser.
Téléchargement des données en provenance d’OSM
Les données géographiques OSM du monde entier sont téléchargeables en un seul fichier (environ 39GB) sur le serveur officiel de la fondation. Une mise à jour de cette base de donnée est effectuée toutes les semaines et est également disponible en format "diff".
Pour éviter d’avoir à télécharger les données de l’ensemble du globe, d’autres sites proposent en téléchargement un découpage plus fin (continent/pays/région) de cette base.
Voici une liste non exhaustive des sites proposant ce découpage :
- https://www.geofabrik.de/
- https://extract.bbbike.org/
- https://mapzen.com/documentation/metro-extracts/
- ...
Les données téléchargées sont au format PBF (Protocol buffer Binary Format) basé sur le format binaire protobuf de Google. Cet encodage est moins volumineux et plus rapide à lire et à écrire que des fichiers XML compressés au format bzip ou gzip (également disponible sur le site OSM).
Intégration des données OSM dans une base de données spatiale locale
- Construction de notre base de données postgreSQL/postGIS
PostgreSQL est un système de gestion de bases de données relationnelles et objets (SGBD). Ce SGBD possède une extension pour gérer les bases de données spatiales nommées postGIS (Geografic Information System). Elle permet la manipulation d’informations géographiques sous forme de géométries (points, lignes, polygones), identifiées par des tags conformément au standard établis par l’Open Geospatial Consortium. Cette extension permet d’héberger une BDD géographique comme OSM.
Voici la procédure à suivre pour installer la base de données postgreSQL/postGIS :
Installation du paquet postgreSQL :
moi@machine$ sudo apt-get install postgresql-<version>-postgis
Utilisez l'utilisateur de postgreSQL pour pouvoir exécuter les commandes qui suivent :
moi@machine$ sudo -u postgres -i
"postgres" est l'utilisateur du serveur postgreSQL. Le but étant simplement de constituer un rendu de tuiles en local et non de mettre à disposition cette BDD à d'autres utilisateurs, j’utilise donc directement cet utilisateur pour constituer la base de données OSM postgreSQL/postGIS.
Creation de la base de donnée osm :
postgres@machine$ createdb -E UTF8 -O postgres osm
postgres = nom d’utilisateur
osm = base de données créée
Lien des extensions postgis à la base de donnée osm :
postgres@machine$ psql -d osm -f /usr/share/postgresql/<version>/contrib/postgis.sql postgres@machine$ psql -d osm -f /usr/share/postgresql/<version>/contrib/postgis_comments.sql postgres@machine$ psql -d osm -f /usr/share/postgresql/<version>/contrib/spatial_ref_sys.sql
Ouverture de l’outil d’édition en ligne de commande postgreSQL pour la BDD OSM :
postgres@machine$ psql -d osm osm=#
Vérification de la présence des tables « geography_columns » et « geometry_columns » spécifique à la base de donnée spatiale postGIS :
osm=# \d (permet d’observer la création des tables / défaut suite à l’intégration de postGIS) List of relations Schema | Name | Type | Owner --------+--------------------+-------+---------- public | geography_columns | view | postgres public | geometry_columns | view | postgres osm=# \q (sortie de l’éditeur de commande de postgreSQL) postgres@machine$ (ctrl-D) (retour à notre compte utilisateur standard) moi@machine$
Notre base de données est maintenant prête à recevoir les données en provenance de notre fichier "<carte>.osm.pbf".
- Chargement de notre BDD avec l’outil osm2pgsql
C’est au travers de l’outil osm2pgsql que nous allons réaliser le transfert du fichier <carte>.osm.pbf dans notre base de données postGIS.
Chargement du paquet osm2pgsql :
moi@machine$ sudo apt install osm2pgsql
Passage en utilisateur postgres pour lancer la commande osm2pgsql :
moi@machine$ sudo -u postgres -i postgres@machine$
Le chargement de la base de donnée doit se faire via le compte utilisateur du serveur de BDD.
Lancement de l’outil osm2pgsql pour la conversion de données ".osm.pbf" vers la base postGIS :
postgres@machine$ osm2pgsql -m -s -U postgres -d gis carte.osm.pbf
NB :
Le root directory de l'utilisateur postgres est /var/lib/postgresql. Afin d’éviter les problèmes de droit d’accès au fichier, le fichier <carte>.osm.pbf sera à placer au préalable dans cette arborescence (ex : /var/lib/postgresql/data/<map>.osm.pbf).
La BDD sur postGIS est maintenant constituée. On peut vérifier la présence des nouvelles tables créées à la suite de l’importation.
postgres@machine$ psql -d osm osm=# osm=# \d List of relations Schema | Name | Type | Owner --------+--------------------+-------+---------- public | geography_columns | view | postgres public | geometry_columns | view | postgres public | planet_osm_line | table | postgres public | planet_osm_nodes | table | postgres public | planet_osm_point | table | postgres public | planet_osm_polygon | table | postgres public | planet_osm_rels | table | postgres public | planet_osm_roads | table | postgres public | planet_osm_ways | table | postgres public | spatial_ref_sys | table | postgres
Transformation de BDD locale en tuiles grâce à l'outil Mapnik
Notre BDD est maintenant constituée et stockée sur notre ordinateur. Nous allons maintenant transformer l’information géographique en tuiles dont l’apparence sera figée.
Chaque tuile sera identifiée en fonction de sa position géographique et stockée dans un dossier correspondant à son niveau de zoom.
Nous utilisons l’outil Mapnik pour effectuer cette transformation.
Utilisation de Mapnik
Mapnik est constitué d’un ensemble d’outils écrits en C++ et Python permettant la transformation de tuiles.
Récupération des fichiers Mapnik depuis le dépot subversion d'OSM :
$ svn checkout https://svn.openstreetmap.org/applications/rendering/mapnik $ cd mapnik
Le fichier README dans le dossier principal donne des informations intéressantes sur l’ensemble des outils disponibles dans Mapnik.
Récupération des shapefiles :
$ mkdir world_boundaries/ $ ./get-coastlines.sh
L’exécution de ce script permet la récupération de l’ensemble des "shapefiles" nécessaires à la construction de la carte dans le fichier world_boundaries. Ces shapefiles seront ensuite exploités par le dernier script de réalisation de tuile.
Note sur les shapefiles :
Les shapefiles sont largement utilisés pour contenir des données vectorielles géographiques. Ils sont en fait une collection de différents fichiers comme nous l’observons dans le dossier "world_boundaries". Ces shapefiles (.shp) sont accompagnés de fichiers de même nom aux suffixes différents (".shp", ".shx", ".dbf", ".prj",".cpg",".index") et contiennent des informations complémentaires aux "shapefiles".
Pour les niveaux de zoom les plus bas, Mapnik utilise des données externes sous forme de shapefiles afin de représenter :
- les lignes côtières
- les frontières
- les grandes villes
- …
L’affichage des lignes côtières est souvent plus complexe. Beaucoup de logiciels de rendu ont besoin de polygones réellement fermés pour générer un rendu de carte correct. La complexité, la constance et le besoin d’établir une géométrie robuste font que ces types d’informations sont stockées au format "shapefile".
Ce format permet également de répondre au besoin de partage des informations importantes pour d'autres systèmes de gestion de données géographiques (QGIS par exemple).
Edition et exécution du script "set-mapnik-env"
Ce script permet de définir l’ensemble des variables d’environnement nécessaires à la construction des tuiles. Tous les paramètres à renseigner sont intégrés à l'intérieur du script.
Définition des variables d’environnement du shell :
$ source ./archive/set-mapnik-env
Vérification des variables d’environnement MAPNIK :
$ env | grep MAPNIK MAPNIK_TILE_DIR=/home/widev/mapnik/tiles_europe/ MAPNIK_DBPORT=5432 MAPNIK_SYMBOLS_DIR=/home/widev/mapnik/symbols/ MAPNIK_DBPASS=smile MAPNIK_MAP_FILE=/home/widev/mapnik/osm.xml MAPNIK_WORLD_BOUNDARIES_DIR=/home/widev/mapnik/world_boundaries/ MAPNIK_PREFIX=planet_osm MAPNIK_DBNAME=osm MAPNIK_DBHOST=localhost MAPNIK_DBUSER=postgres
Constitution du fichier osm.xml :
$ ./generate_xml.py --dbname osm --host 'localhost' --user postgres --port 5432 --password 'your_pwd'
NB : Il y a une redondance sur les paramètres d’entrées caractérisés par les variables d’environnement et les paramètres du script ./generate_xml.py que je ne sais pas expliquer.
La présence à la fin du script d’une variable bbox (bounding box) permet de définir les frontières de génération des tuiles. La fonction render_tiles() qui suit permet de lancer le processus de génération de ces tuiles. L’utilisateur est donc libre de définir plusieurs zones géographiques dont il voudra obtenir des tuiles. Il n’aura qu’a définir plusieurs bounding box, toutes suivies de la fonction render_tiles().
Dans les cas où les informations géographiques contenues dans la BDD (stockée dans postGIS) ne couvre pas la totalité de la bbox, on observera tout de même sur les zones non couvertes par cette BDD le dessin des continents correspondant à l'expression des shapefiles.
Le formalisme de la bbox est le suivant :
bbox = (gauche, bas, droite, haut) bbox = (Longitude min, Latitude min, Longitude max, max Latitude)
La latitude est un nombre décimal compris entre -90.0 et 90.0.
La longitude est un nombre décimal compris entre -180.0 et 180.0.
La formalisme de la fonction render_tiles() est la suivante :
render_tiles(bbox, mapfile, tile_dir, <zoom min>, <zoom_max>, "nom_tuiles")
NB :
Le site geofabrik.de fournit un assistant graphique qui permet de déterminer à partir d’une délimitation géographique :
- les valeurs de la bounding box
- la plage de zoom nécessaire pour permettre l’observation de cette zone
- l’espace mémoire des tuiles nécessaires pour chacun et pour l’ensemble des zooms
- le nombre de tuiles pour chacun des niveaux de zoom et pour l’ensemble
Nous obtenons donc pour Paris la bounding box et la fonction render_tiles() suivante :
# Paris+ bbox = (2.11, 48.69, 2.59, 49.01) render_tiles(bbox, mapfile, tile_dir, 9, 18, "Paris+")
Ces deux lignes seront à insérer à la fin du script genrate_tiles après avoir commenté les autres couples bbox/render_tiles().
Mapnik met à disposition un script permettant de générer une image à partir de la variable bounding box. Un premier test de réalisation d’image de 256 x 256 pixel au niveau de zoom le plus bas (cad le plus petit) peut être intéressant pour contrôler au préalable les frontières bbox et l’apparence avant d’effectuer le rendu de tuile final.
Les variables à rentrer pour la génération de l’image sont :
bounds = (2.11, 48.69, 2.59, 49.01) z = 9 imgx = 256 * z imgy = 256 * z
Après exécution du script generate_image.py, nous obtenons cette l’image :
Cette image nous confirme donc la bonne définition de notre bounding box. Nous pouvons maintenant générer l’ensemble de nos tuiles via la commande :
./generate_tiles.py
Voici un aperçu des tuiles générées dans un des dossiers correspondant au niveau de zoom :
A titre d'information, mon ordinateur i5 équipé de 16 Gio de RAM a passé un peu plus de 3 heures à générer les tuiles pour la précédente bounding box. L'espace occupé par l'ensemble des tuiles générées est de 1,5 Gio. Pour "tuiler" l'ensemble de la map monde, nous aurions généré un volume de tuile dont l'espace aurait été supérieur à 100 Gio.
Les manipulations manuelles sont ici nombreuses et il est possible de les automatiser (scripts ou outils d’exécution de plus haut niveau).
Note sur les stylesheets :
Les apparences sont présentes dans le dossier mapnik/inc/. Comme cité précédemment, l’ensemble des tuiles est obtenu par la combinaison des données géographiques et de feuilles de styles au travers de l’outil de rendu qu’est le script generate_tiles.py. Les feuilles de style sont aussi présentes dans le sous dossier manik/inc/. Elles permettent de définir l’apparence des cartes en fonction :
- de la nature de l’information géographique
- du niveau de zoom
NB : Ceux pour qui la modification des apparences via les feuilles de style est rédhibitoire, il existe des logiciels qui permettent l’édition de carte en mode graphique. Le wiki d’Open Street Map présente les différentes alternatives (lien).
Ces logiciels d’édition de carte contraignent le format des stylesheets et/ou la méthode de génération des cartes. Il est donc possible que certains logiciels soient incompatibles avec la méthode de génération des cartes avec Mapnik.
Exploitation de nos tuiles sur Raspberry Pi
Nos tuiles peuvent maintenant être visualisées sur système embarqué.
Nous pouvons exploiter nos tuiles de 2 manières différentes: via une API web ou locale. Nous avons choisi pour cette article l'utilisation d'une API web.
Parmi les API web de visualisation dynamique de cartes, il y en a 2 qui sont plus largement utilisées :
- OpenLayer, mature et riche de nombreuses fonctions
- Leaflet, plus rapide à mettre en place avec des bibliothèques beaucoup plus légères
Nous utiliserons ici l’API Leaflet.
Concernant les APIs locales, vous pouvez en utiliser une parmi celles citées sur le wiki OSM (lien).
L’API Leaflet étant une API Web, nous devons donc :
- lancer un serveur HTTP
- constituer un ficher index.html permettant :
- de charger l’API Leaflet dans le cache de l'explorateur web
- de pointer le dossier contenant les tuiles préalablement construites
Voici les étapes à suivre pour pouvoir visionner dynamiquement vos tuiles :
- Créer un dossier www/ permettant d’accueillir l’environnement du serveur web.
- Générer dans ce dossier un fichier index.html avec le code suivant (le code est issu d'un code exemple fourni par Leaflet (lien)) :
<!DOCTYPE html> <html> <head> <title>Quick Start - Leaflet</title> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link rel="shortcut icon" type="image/x-icon" href="docs/images/favicon.ico" /> <link rel="stylesheet" href="leaflet.css" /> <style> body { padding: 0; margin: 0; } </style> </head> <body> <div id="mapid" style="width: 100%; height: 100vh;"></div> <script src="leaflet.js"></script> <script> var mymap = L.map('mapid').setView([48.69, 2.59], 9); L.tileLayer('http://localhost:8000/tiles/{z}/{x}/{y}.png', {maxZoom: 18}).addTo(mymap); </script> </body> </html>
Pour utiliser Leaflet offline, il faut télécharger la version stable de Leaflet (lien) et extraire l'archive dans le dossier www/ ou se trouve le fichier index.html. Cette archive contient 3 éléments principaux :
- leaflet.js : Le code JavaScript de l'application Leaflet
- leaflet.css : La feuille de style pour l'affichage de l'application
- images/ : Le dossier contenant les images référencées par leaflet.css
Démarrage du serveur web :
$ cd www/ $ python -m SimpleHTTPServer 8000
Nous utilisons un serveur web python pour afficher rapidement nos API Web locale.
Dans notre explorateur web, nous entrons dans l’URL la ligne suivante :
http://localhost:8000/
Et nous obtenons l’affichage de nos tuiles préalablement générées sur notre Raspberry Pi avec le navigateur Chromium.
CONCLUSION
Cet article vous a montré une méthode pour créer et visualiser dynamiquement vos cartes sur une architecture embarquée en mode hors ligne.
Au travers des rendus obtenus, on peut se rendre compte de la richesse d’information que contient la base de données Open Street Map. Ce modèle coopératif est une solution pérenne pour maintenir la liberté que nous avons d'exploiter les informations géographiques du monde entier (éducation, services, commerces, …).
Le travail décrit dans cet article peut être le point de départ pour :
- styliser vos cartes
- rajouter des informations à votre carte en statique ou en dynamique
- intégrer des informations de géocodage dans votre carte afin de pouvoir :
- afficher des informations relatives à une adresse (ex : Nominatim)
- intégrer cette solution de géocodage au sein d’un outil de création d’itinéraire (ex : OSRM ou pg_routing.org)
Le monde de l’Open Data est ici complémentaire à celui de l’Open Source. L'exemple d'Open Street Map est ici à suivre au regard des futures évolutions technologiques propriétaires que nous désirerons exploiter.
Merci pour cet article très complet.
Certaines applications mobiles fonctionnent en mode non connectées.
C'est le cas notamment de OsmAnd ( https://osmand.net/ ). Les cartes doivent être préalablement chargées. Ce sont des fichiers .obf ( https://wiki.openstreetmap.org/wiki/OsmAndMapCreator )
Avez-vous une idée comment ces fichiers sont exploités ? Est ce que le calcul des tuiles est dynamique et fait en temps réel ?
Merci pour vos réponses.