9. Preview Route

The following tutorial will demonstrate how to request a route using the UNL SDK and preview it on the map. It will exemplify the request of both: outdoor-to-outdoor and outdoor-to-indoor routes.

First, lets start by adding this new option in the ActionSheet.js file:

<button id="preview-route-button" class="action-sheet-button">
Preview route
</button>

Next, we need to initialize the mapbox source and layers for the route polyline:

export const renderRoute = (map) => {
map.addSource("route", {
type: "geojson",
data: {
type: "Feature",
geometry: {
type: "LineString",
coordinates: [],
},
},
});
map.addLayer(
{
id: "route",
type: "line",
source: "route",
paint: {
"line-color": "#32C5FF",
"line-width": 5,
},
},
"routeDestinationMarker"
);
};

Same for the two markers that highlight the starting and the ending point of the route. The coordinates of the starting point were hardcoded for the purpose of this tutorial:

const ROUTE_STARTING_POINT = [13.38010311126709, 52.5201416015625];
export const renderRouteSourceMarker = (map) => {
map.addSource("routeSourceMarker", {
type: "geojson",
data: {
type: "Feature",
geometry: {
type: "Point",
coordinates: ROUTE_STARTING_POINT,
},
},
});
map.addLayer({
id: "routeSourceMarker",
type: "symbol",
source: "routeSourceMarker",
layout: {
"icon-image": "route_source_icon",
"icon-size": 0.7,
"icon-allow-overlap": true,
},
});
};
export const renderRouteDestinationMarker = (map) => {
map.addSource("routeDestinationMarker", {
type: "geojson",
data: {
type: "Feature",
geometry: {
type: "Point",
coordinates: [],
},
},
});
map.addLayer({
id: "routeDestinationMarker",
type: "symbol",
source: "routeDestinationMarker",
layout: {
"icon-image": "route_destination_icon",
"icon-size": 0.5,
"icon-offset": [0, -40],
"text-font": ["Fira GO Regular"],
"icon-allow-overlap": true,
},
});
};

Now that we have the visual elements in place, lets proceed with requesting the actual route. We have to add a new function in the unlApi.js file which calls the route method of the sdk:

export const fetchRoute = (projectId, routeRequest) => {
return unlApi.routingApi.route(projectId, routeRequest);
};

Next, lets implement the function which builds the routeRequest object, and pass it together with the projectId to the function above. The routeRequest object contains an array of waypoints. The first element of the array contains the coordinates of the starting point. Whilst for the second element, which represents the destination, a function named buildDestinationWaypoint is called which determines wether the point is an indoor or an outdoor location and build the object accordingly:

export const previewRoute = async (map) => {
const projectId = config.PROJECT_ID;
const destinationMapSource = map.getSource("unlCell");
const destinationCell = destinationMapSource._data.geometry.coordinates;
if (!destinationCell.length) {
alert("Select the destination cell!");
} else {
const sourceCoordinates =
map.getSource("routeSourceMarker")._data.geometry.coordinates;
const routeRequest = {
preference: "fastest",
waypoints: [
{
type: "point",
coordinates: sourceCoordinates,
},
{ ...buildDestinationWaypoint(destinationMapSource) },
],
};
const route = await fetchRoute(projectId, routeRequest);
const routeCoordinates = route.overview.linestring.map((coords) => {
const splittedCoords = coords.split(",");
const lng =
routeRequest.waypoints[1].type === "indoor"
? splittedCoords[0]
: splittedCoords[1];
const lat =
routeRequest.waypoints[1].type === "indoor"
? splittedCoords[1]
: splittedCoords[0];
return [Number(lng), Number(lat)];
});
map.getSource("route").setData({
type: "Feature",
geometry: {
type: "LineString",
coordinates: routeCoordinates,
},
});
map.setLayoutProperty("routeSourceMarker", "visibility", "visible");
const destinationCellCorner = destinationCell[0][1];
const destinationGeohash = UnlCore.encode(
destinationCellCorner[1],
destinationCellCorner[0],
9 // geohash precision
);
const destinationCoordinates = geohashToCoordinates(destinationGeohash);
updateDestinationMarkerPosition(map, destinationCoordinates);
}
};

Below is the buildDestinationWaypoint function:

const buildDestinationWaypoint = (destinationMapSource) => {
const destinationSourceProperties = destinationMapSource._data.properties;
const destinationCellCorner =
destinationMapSource._data.geometry.coordinates[0][0];
const destinationGeohash = UnlCore.encode(
destinationCellCorner[1],
destinationCellCorner[0],
9 // geohash precision
);
if (destinationSourceProperties.venueId) {
// indoor waypoint
const indoorWaypoint = {
type: "indoor",
venueId: destinationSourceProperties.venueId,
unitId: destinationSourceProperties.id,
levelId: destinationSourceProperties.level_id,
geohash: destinationGeohash,
};
return indoorWaypoint;
} else {
// outdoor waypoint
return {
type: "geohash",
geohash: destinationGeohash,
};
}
};

Finally, add the click event listener to the menu option and call the previewRoute function:

document
.getElementById("preview-route-button")
.addEventListener("click", () => {
previewRoute(map);
});

Let's see how it looks: