Let’s look at a situation which involves the precise generation of a route between two locations. To solve this, we can use turf js, in several ways. Without using turf js, the implementation would lack accuracy on the roadway.
What is Turf js and Why use it?
Turf js is an open-source library. Generally, it is used for spatial analysis. It can also be used for spatial operations and has built-in functions to create GeoJSON data. The reason why choose turf js instead of the regular GeoJSON collection:
- To have curved lines instead of straight lines with straight cuts.
- Ease of plotting the routes
What is MapBox GL and MapBox Optimization API?
A robust and flexible open-source mapping framework called Mapbox GL is used to make dynamic and editable maps for the web. It is based on WebGL (Web Graphics Library) technology, which enables it to generate maps.
The Mapbox Optimisation API is a RESTful API that allows you to construct optimised routes between several places. It is a powerful tool for planning deliveries, field service calls, and other types of multi-stop trips.
Getting started:
- Mapbox Optimization API.It is an Optimization API that will help you generate optimal delivery routes for multiple stops across an entire fleet.
- Example use-case:A courier company can use the Mapbox Optimization API to plan the most efficient route for delivering packages to a set of addresses. The API can take into account factors such as traffic conditions, package weight, and delivery deadlines to generate the most efficient route.
- it is an Optimization APIthat will help you generate optimal delivery routes for multiple stops across an entire fleet.
- Mapbox account and access token.you need to Sign up for an account at com/signup. You can find your access tokens on your Account page.
Steps to create a route using Optimized API using turf js:
- Create a folder for e.g. trip-tool
- Install turf js and mapbox-gl
npm i mapbox-gl npm i @turf/turf
- Create a js file inside that folder for e.g. TripFinder.js
- Initialize the map with Mapbox GL JS inside the js file,
import React,{ useEffect,useRef } from 'react';
import mapboxgl from 'mapbox-gl';
import 'mapbox-gl/dist/mapbox-gl.css';
mapboxgl.accessToken = 'YOUR_MAPBOX_ACCESS_TOKEN';
function TripFinder() { //intializing the longititude and lattitudes
const coordinates = [-96.5108, 39.1184];
const mapContainer = useRef(null);
useEffect(() => { //intialize the map
const map = new mapboxgl.Map({
container: mapContainer.current,
// container id
style: 'mapbox://styles/mapbox/light-v11',
// stylesheet location
center: coordinates,
// starting position
zoom: 12,
// starting zoom
}); }, []);
return ( <>
<div id="map" ref={mapContainer}>
</div> </> ); }
export default TripFinder;
and create a new CSS file in trip-tool e.g: TripFinder.css as shown below:
body { margin: 0; padding: 0; } #map { position: absolute; top: 0; bottom: 0; right: 0; left: 0; }
Now, when you open the file in your browser, you can see the map aligned at the centre.
- Plot the from (source) and to (destination) location on the map. For this, you can use the mapboxgl.Marker and make sure the plotting is done only after the map is loaded, so this would be returned in a callback method: map.on
e.g. Consider the From location to be Phoenix, US [-112.073555,33.44793] and To location to be Texas, US [-110.965274,32.228759],
Create a collection using turf js for marking:
// Create a GeoJSON feature collection for the warehouse
from = turf.featureCollection([turf.point(FromLocation)]);
to = turf.featureCollection([turf.point(ToLocation)]);
After creating the collection, add markers using these collections
const addMarker= useCallback(() => {
map.current.on('load', async () => {
map.current.addLayer({
id: 'from',
type: 'circle',
source: {
data: from,
type: 'geojson',
},
paint: {
'circle-radius': 10,
'circle-color': 'blue',
'circle-stroke-color': 'blue',
'circle-stroke-width': 3,
},
});
map.current.addLayer({
id: 'to',
type: 'circle',
source: {
data: to,
type: 'geojson',
},
paint: {
'circle-radius': 10,
'circle-color': 'red',
'circle-stroke-color': 'red',
'circle-stroke-width': 3,
},
});
});
});
Hoorah 😊 ..! we are in the final steps!
- Let us plot the route between the two locations using Mapbox-optimized API v1.
Few things to know before using this API:
In an application that generates routes between several points, there are many different ways you could generate the points: a user could input addresses, a user could select coordinates on the map, or you could pull data in from an external source.
A request to the Optimization API must contain 2–12 coordinates:
- We won’t be using the default behaviour of the Optimization API, which is to return round-trip routes. This means that the first coordinate is both the start and end point of the trip.
- We will be using another Mapbox API to get the coordinates between the two locations:
https://api.mapbox.com/directions/v5/mapbox/driving/from,to?steps=true&geometries=geojson&access_token=${mapboxgl.AccessToken}
- The pick-up location will also be counted toward the 12-coordinate limit.
- As a result, the user can select up to 10 points excluding the Start coordinate and Destination coordinate.
As a best practice, let us have the API fetch a separate file.
Let us get the Coordinates from the Mapbox API:
const response = await fetch(
`https://api.mapbox.com/directions/v5/mapbox/driving/${FromLocation.join(',')};
${ToLocation.join(',')}?steps=true&geometries=geojson&access_token=${mapboxgl.accessToken
}`,
{ method: 'GET' });
let data = await response.json();
data = data.routes[0];// to get the coordinates between the source and destination
let coordinates = data.geometry.coordinates;
console.log(coordinates);
- After getting the coordinates, we plot it using the Optimized API. At the same time, we need to ensure that the param to optimized API’s default value is 12, hence we splice this array of coordinates to a range of 12
const len = coordinates.length;
const distributions = [1, 2];
if (len > 12) {
coordinates.splice(1, coordinates.length - 12);
}
The Optimization API returns a duration-optimized route between two and 12 input coordinates.
An Optimization API request has two required parameters:
- Profile: This is the mode of transportation used by the request. Choose from one of the Mapbox Directions routing profileIDs (mapbox/driving, mapbox/walking, mapbox/cycling, and mapbox/driving-traffic). In this blog, you will use the mapbox/driving profile.
- Coordinates: This is a semicolon-separated list of {longitude}, {latitude} coordinates. These must be between 2 and 12 coordinates, and the first coordinate is the start and end point.
The Optimization API request will look like this:
https://api.mapbox.com/optimized-trips/v1/mapbox/driving/-122.42,37.78;-122.45,37.91;-122.48,37.73?access_token=YOUR_MAPBOX_ACCESS_TOKEN
An Optimization API request has several optional parameters:
- Roundtrip: By default Optimization APIwill follow roundtrip to be true. In order to change that default behaviour we need to pass the parameters’ source and destination
- Geometries: This parameter tells the Optimization API what format the returned geometry should be. For this app, you will request geometries=geojson to get GeoJSON
- Steps: This parameter tells the Optimization API whether or not it should return turn-by-turn instructions.
- Distributions: This is a semicolon-separated list of number pairs that correspond with the coordinates list.
For further details, refer: https://docs.mapbox.com/api/navigation/optimization-v1/
add the following snippet after we fetch the coordinates,
return await fetch(
`https://api.mapbox.com/optimized-trips/v1/mapbox/driving/${coordinates.join(';'
)}?distributions=${distributions}&overview=full&steps=true&geometries=geojson&roundtrip=false&source=first&destination=last&access_token=${
mapboxgl.accessToken}`,
{ method: 'GET' }
);
Great progress so far! 👌
- Plot the resultant coordinates using turf js:
const query = await fetchLoaction();
const response = await query.json();
const newTurfCollection = turf.featureCollection([]);
// Update the `route` source by getting the route source
// and setting the data equal to routeGeoJSO
const routeGeoJSON = turf.featureCollection([
turf.feature(response.trips[0].geometry),
]);
// if the route already exists on the map, we'll reset it using setData
if (map.current.getSource('route')) {
map.current.getSource('route').setData(routeGeoJSON);
}
else {
// adding the layer of routeline-active to plot the result
map.current.addSource('route', {
type: 'geojson',
data: newTurfCollection,
});
map.current.addLayer(
{
id: 'routeline-active',
type: 'line',
source: 'route',
layout: {
'line-join': 'round',
'line-cap': 'round',
},
paint: {
'line-color': '#0E3464',
'line-width': ['interpolate', ['linear'], ['zoom'], 12, 3, 22, 12],
},
},
'waterway-label'
);
}
Boom!😌
Let us wrap here ..!👋
Also, here’s another example of how turf js works (this includes certain locations from Mapbox API which is not a part of this documentation):
Code-repo URL: https://github.com/keerthivasanm20/optimizedmapRoute