Makina Blog

Le blog Makina-corpus

Géné­rer un fichier PMTiles avec Tippe­ca­noe


Exemple de géné­ra­tion et d’af­fi­chage d’un jeu de tuiles vecto­rielles en PMTiles à partir de données publiques.

Sommaire

Prérequis tech­niques

Pour ne pas avoir à instal­ler des dépen­dances complexes, nous utili­sons Tippe­ca­noe à travers Docker et des conte­neurs éphé­mères.
Nous avons donc besoin d’avoir une image Docker de Tippe­ca­noe que nous pouvons géné­rer nous même à partir des sources :

git clone https://github.com/felt/tippecanoe
cd tippecanoe
docker build -t tippecanoe:latest .

Après le trai­te­ment qui dure quelques minutes, vous pouvez véri­fier la présence d’une image de Tippe­ca­noe avec la commande : docker images

Récu­pé­rer et prépa­rer les données

Le jeu de données qui est utilisé pour cet exemple est une base des aména­ge­ments cyclables de France Métro­po­li­taine, numé­ri­sés dans OpenS­treet­Map et trai­tés par Geovelo.

On télé­charge le jeu de données au format GeoJ­SON :

wget https://www.data.gouv.fr/fr/datasets/r/d03abb03-9197-4a4f-b758-5062595eb9ce -O amenagements.geojson

À noter : nous utili­sons ici des données GeoJ­SON pour produire les tuiles, mais Tippe­ca­noe accepte aussi des données CSV ou Flat­Geo­buf.

Pour faci­li­ter le trai­te­ment de la source de données, nous allons conver­tir le GeoJ­SON origi­nal en un fichier GeoJ­SON-NL : au lieu d’un objet racine unique de type Featu­re­Col­lec­tion conte­nant ensuite toutes les Features, nous aurons un fichier dont chaque ligne est une Feature. Cela permet­tra à Tippe­ca­noe de paral­lé­li­ser ses trai­te­ments.

docker run -it --rm -v .:/app tippecanoe:latest \
    tippecanoe-json-tool amenagements.geojson > amenagements.geojson-nl

GeoJ­SON :

Capture d'écran d'un exemple de fichier GeoJSON

GeoJ­SON-NL :

Capture d'écran d'un exemple de fichier GeoJSON-NL

D’autres possi­bi­li­tés pour faire cette même conver­sion :

# Avec jq : https://jqlang.github.io/jq/
cat amenagements.geojson | jq -c ".features[]" > amenagements.geojson-nl

# Avec GDAL : https://gdal.org/programs/ogr2ogr.html
ogr2ogr -f GeoJSONSeq amenagements.geojson-nl amenagements.geojson

Géné­rer la pyra­mide de tuiles en PMTiles

On peut ensuite produire notre fichier PMTiles :

docker run -u $UID:$GID -it --rm -v .:/app tippecanoe:latest \
  tippecanoe --read-parallel \
  --maximum-zoom=g --drop-densest-as-needed --extend-zooms-if-still-dropping \
  --output=amenagements.pmtiles \
  amenagements.geojson-nl

À noter : Il est impor­tant para­mé­trer Tippe­ca­noe avec des options de simpli­fi­ca­tion adap­tées au type des données : réseau de linéaires, géomé­tries de zones, ensemble de points, …

Après quelques minutes, le fichier .pmtiles est créé, et on peut visua­li­ser le résul­tat en le faisant glis­ser sur https://proto­maps.github.io/PMTiles/.

Pour un GeoJ­SON initial de 235 Mo, nous obte­nons ici une pyra­mide complète de tuiles de 40 Mo !

Affi­cher le fichier PMTiles

Nous partons d’un simple fichier carte.html, dans lequel on va char­ger MapLibre GL JS et un petit complé­ment pour le rendre capable d’uti­li­ser le fichier PMTiles :

<script src="https://unpkg.com/maplibre-gl@4/dist/maplibre-gl.js"></script>
<script src="https://unpkg.com/pmtiles@3/dist/pmtiles.js"></script>
<link href="https://unpkg.com/maplibre-gl@4/dist/maplibre-gl.css" rel="stylesheet" />

Pour anti­ci­per un peu le rendu, on ajoute un peu de CSS :

<style>
  html { box-sizing: border-box; font-size: 16px; }
  *, *:before, *:after { box-sizing: inherit; }
  html, body { margin: 0; padding: 0; }
  pre { white-space: pre-wrap; }
</style>

Dans le corps de la page, on place la balise où se char­gera la carte :

<div id="map" style="height: 100vh;"></div>

Puis enfin, le script qui va permettre de créer la carte :

<script>
  // Ajoute à MapLibre la gestion des PMTiles
  const protocol = new pmtiles.Protocol();
  maplibregl.addProtocol('pmtiles', protocol.tile);

  // Instancie la carte avec un fond de démo (coloré)
  const map = new maplibregl.Map({
    container: 'map',
    style: 'https://demotiles.maplibre.org/style.json',
    center: [1.5, 47],
    zoom: 6,
  });

  // Crée une Popup qu'on affichera au survol des Features
  const popup = new maplibregl.Popup({ closeButton: false, closeOnClick: false });

  // Une fois que la carte est instanciée…
  map.on('load', () => {
    
    // Ajoute notre fichier PMTiles en tant que source vectorielle
    map.addSource('id-de-ma-source', {
      type: 'vector',
      // La syntaxe ici est "pmtiles://" puis le chemin d'accès au fichier.
      // Ce chemin peut-être relatif ("./fichier", "fichier", "dossier/fichier"),
      //  absolu ("/fichier", "/fichier", "/dossier/fichier")
      //  ou distant ('https://domaine.tld/cheminfichier")
      url: 'pmtiles://amenagements.pmtiles',
    });

    // Ajoute à la carte une couche linéaire utilisant notre source PMTiles
    map.addLayer({
      id: 'id-de-ma-couche',
      source: 'id-de-ma-source',
      // "source-layer" est le nom de notre couche "dans" le fichier PMTiles
      //   Si on ne l'a pas précisé à la création du PMTiles, Tippecanoe génère
      //   un identifiant à partir du fichier source.
      //   Il est facile de vérifier les couches d'un PMtiles en le chargeant
      //   sur https://protomaps.github.io/PMTiles
      'source-layer': 'amenagementsgeojsonnl',
      type: 'line',
      paint: {
        'line-color': '#198EC8',
        'line-width': 3,
        'line-opacity': 0.7,
      },
      layout: {
        'line-join': 'round',
        'line-cap': 'round',
      },
    });

    // Au survol de notre couche, affiche une Popup
    map.on('mouseenter', 'id-de-ma-couche', ({ lngLat, features } = {}) => {
      map.getCanvas().style.cursor = 'pointer';
      popup
        .setHTML(
          features.map(feature => '<pre>' + JSON.stringify(feature.properties, null, 2) + '</pre>').join('</br >')
        )
        .setLngLat(lngLat)
        .addTo(map);
    });
    map.on('mouseleave', 'id-de-ma-couche', () => {
      map.getCanvas().style.cursor = '';
      popup.remove();
    });
  });
</script>

Pour affi­cher votre page HTML avec la carte, il sera néces­saire d’uti­li­ser un serveur http suppor­tant les entêtes HTTP Range

À noter : char­ger le fichier html direc­te­ment dans le navi­ga­teur ne fonc­tion­nera pas.

Par exemple :

# Avec Nodejs :
npx serve
# ou bien 
npx http-server
Capture d'écran d'une portail de la carte générée avec ce tutoriel.

 

Liens utiles

Formations associées

Formations SIG / Cartographie

Formation MapLibre

Toulouse Du 17 au 18 octobre 2023

Voir la formation

Formations SIG / Cartographie

Formation QGIS

Nantes Du 2 au 4 avril 2024

Voir la formation

Formations SIG / Cartographie

Formation Développer avec l'écosystème d'OpenStreetMap

Toulouse Du 16 au 17 mai 2023

Voir la formation

Actualités en lien

Image
Article SIG cartographie : Géné­rer un fichier PMTiles à partir d’un MBTiles
24/04/2024

Géné­rer un fichier PMTiles à partir d’un MBTiles

Exemple de conver­sion d’une base de tuiles vecto­rielles MBTiles du Plan Cadas­tral Infor­ma­tisé vers le format PMTiles (Proto­maps).

Voir l'article
Image
Encart article Protomaps : Illustration d'une portion de pyramide de tuiles
14/02/2024

Protomaps, stockez vos pyramides de tuiles plus simplement

Présentation d'un nouveau format de stockage de tuiles cartographiques

Voir l'article
Image
Article : Servir sa couche raster QGIS en tuiles sans effort avec le format PMTiles
25/01/2024

Servir sa couche raster QGIS en tuiles sans effort avec le format PMTiles

Cet article vous présente une approche permet­tant de tuiler et de publier une couche raster fabriquée avec QGIS.

Voir l'article

Inscription à la newsletter

Nous vous avons convaincus