MapBox : Utiliser le « map matching » pour tracer un itinéraire routier à partir de points GPS

Nous allons voir dans cet article comment utiliser l’API MapBox pour faire différentes choses :
– Tracer un trajet à partir de points GPS
– Tracer un itinéraire routier à partir de ces points GPS
– Afficher des points cliquables sur la carte
– Recentrer la carte autour de ces tracés/points

Le résultat que l’on va obtenir est visible ici

Pour commencer il vous faut un « token » d’accès à l’API que vous trouverez dans votre compte MapBox.

Commençons par inclure les fichiers css et js nécessaires à l’utilisation de l’API. Dans la balise « head » de notre page :

<script src='https://api.tiles.mapbox.com/mapbox-gl-js/v0.50.0/mapbox-gl.js'></script>
<link href='https://api.tiles.mapbox.com/mapbox-gl-js/v0.50.0/mapbox-gl.css' rel='stylesheet' />

Le html minimum pour afficher la carte dans le « body » de la page : c’est un div avec un identifiant (ici « map ») ainsi qu’une largeur et une hauteur :

<div id="map" style="width: 100%; height: 400px;"></div>

Tout est prêt, on peux attaquer le code javascript. Nous partons ici du principe que nous avons une liste de coordonnées GPS sur lesquelles on veux travailler, voici celles de mon exemple :

//la liste de nos points GPS, attention dans l'ordre [LONGITUDE, LATITUDE] (et non l'inverse)
var coords = [
    [4.141553499740439, 44.052572457451014],
    [4.143273931900012, 44.05242402365157],
    [4.14427862409957, 44.05275366184478],
    [4.145185210746604, 44.05318932120335],
    [4.143211104911643, 44.053065948966925],
    [4.141692974609214, 44.05368666292508],
    [4.142165043395835, 44.05420327703502]
];

On initialise notre map sur le div #map, et au chargement on appelle nos différentes fonctions qui effectueront les traitements voulus (fonctions que l’on va créées une par une juste après)
Les 4 fonctions sont indépendantes, vous pouvez bien sur appeler uniquement celle qui vous intéresse.

//token mapBox
var accessToken = 'VOTRE-TOKEN-MAPBOX';
mapboxgl.accessToken = accessToken;
//on initialise notre map sur le div #map
var map = new mapboxgl.Map({
    container: 'map',
    style: 'mapbox://styles/mapbox/streets-v10',
    center: [4.141056, 44.050022399999996], //un centre initial [longitude, latitude] (facultatif)
    zoom: 13 //un zoom initial (facultatif)
});
//au chargement de la map
map.on('load', function () {
    //on recadre la carte en fonction de nos différents points GPS
    fitMap(map, coords);
    //on affiche le tracé reliant nos différents points GPS
    displayJourney(map, coords);
    //on affiche l'itinéraire routier correspondant à nos points GPS
    displayJourneyReshaped(map, coords);
    //on affiche des marqueurs à la position de nos points GPS, indiquant leur numéros et recentrant la carte sur eux au clic.
    placeMarkers(map, coords);
});

Pour que cela fonctionne il nous faut bien sur créer les fonctions correspondantes. On attaque avec la première « fitMap » qui va permettre de cadrer la carte afin que toutes les coordonnées qu’on lui fournit soit visible :

function fitMap(map, coords) {
    var bounds = coords.reduce(function (bounds, coord) {
        return bounds.extend(coord);
    }, new mapboxgl.LngLatBounds(coords[0], coords[0]));
    map.fitBounds(bounds, {
        padding: 30 //marge autour des points
    });
}

On passe à « displayJourney » qui trace une ligne droite entre chacun des points donnés :

function displayJourney(map, coords) {
    map.addLayer({
        "id": "journey", //identifiant unique de l'objet
        "type": "line",
        "source": {
            "type": "geojson",
            "data": {
                "type": "Feature",
                "properties": {},
                "geometry": {
                    "type": "LineString",
                    "coordinates": coords
                }
            }
        },
        "paint": {
            "line-color": "#888", //couleur de la ligne
            "line-width": 2 //epaisseur de la ligne
        }
    });
}

C’est déjà pas mal, mais on veux maintenant à la place de ces lignes droites, définir le trajet « réel » utilisable par un automobiliste (ou un cycliste / marcheur selon les paramètres) sur les chemins connus. Attention il vous faut moins de 100 points pour que cela fonctionne. Attention aussi, l’utilisation de cette fonctionnalité est limité a 1000 appel par MapBox avant de devenir payant. Il peut donc être intéressant de stoker le résultat si on doit l’afficher à plusieurs reprise.
Voici la fonction « displayJourneyReshaped » qui lance un appel ajax à l’API Map Matching de MapBox pour calculer l’itinéraire :

function displayJourneyReshaped(map, coords) {
    //on transforme nos coordonées en string pour l'appel de l'API
    var coordsString = coords.join(';');
    //choix du type d'itinéraire que l'on souhaite calculer (par exemple avec "walking" on ne fera pas le tour d'un rond point, avec "driving" si.
    var typeRoute = 'driving'; //cycling, walking, driving-traffic
    var directionsRequest = 'https://api.mapbox.com/matching/v5/mapbox/'+typeRoute+'/' + coordsString + '?geometries=geojson&access_token=' + accessToken;
    var xhr = new XMLHttpRequest();
    xhr.open('GET', directionsRequest);
    xhr.onload = function () {
        if (xhr.status === 200) {
            var response = JSON.parse(xhr.responseText);
            //on récupère la données calculé qui nous permettra d'afficher l'itinéraire
            var route = response.matchings[0].geometry;
            //add layer
            map.addLayer({
                id: 'journeyReshaped', //identifiant unique de l'objet
                type: 'line',
                source: {
                    type: 'geojson',
                    data: {
                        type: 'Feature',
                        geometry: route //utilisation de l'itinéraire
                    }
                },
                paint: {
                    'line-color': "#3399ff", //couleur de la ligne
                    'line-width': 4, //epaisseur de la ligne
                    'line-opacity': 0.7 //opacité de la ligne
                }
            });
        } else {
            //en cas d'erreur ajax
            console.log('Request failed.  Returned status of ' + xhr.status);
        }
    };
    xhr.send();
}

En bonus la fonction « placeMarkers » qui permet d’afficher des points cliquable sur la carte. Pour cela on commence par ajouter un peu de style css à notre page pour l’affichage de nos marqueurs ayant la classe « marker ». A mettre donc dans le style de votre page ou votre fichier css :

.marker {background: #888;width: 22px;height: 22px;border-radius: 50%;text-align: center;color:#fff;}
.marker:hover{background: #ff0;color:#000;}

Et enfin la fonction d’affichage et clic des différents points :

function placeMarkers(map, coords) {
    var markers = [];
    //pour chaque point GPS dans coords
    coords.forEach(function (coord, index) {
        //creation d'un div avec la classe 'marker' pour l'affichage du marker
        var el = document.createElement('div');
        el.className = 'marker';
        el.setAttribute('data-index', index);//on stocke son numéro pour l'utilisation au click
        //creer un élément pour indiquer le numéro du marquer dans celui-ci
        var content = document.createTextNode(index);
        el.appendChild(content);
        //on ajoute les marquers sur notre carte
        markers[index] = new mapboxgl.Marker({element: el}).setLngLat([coord[0], coord[1]]).addTo(map);
        //au clic sur chacun d'eux on recentre la carte sur sa position
        el.addEventListener("click", function (e) {
            map.flyTo({center: markers[e.target.dataset.index].getLngLat()});
        });
    });
}

Je pense que c’est un bon début pour travailler avec MapBox, maintenant à vous le tour !