Tutorials / Toll Cost Map with HERE Javascript SDK 3.1 & Rest API
Last Updated: July 05, 2020
Quick Introduction:
We all love road trips. Wouldn’t it be nice to see the total cost of all the tolls while we are researching the routes to our favorite destination? That is what we are going to build in this tutorial using JavaScript and HERE’s Location Services.
You will create a base map using the Javascript Mapping Library
Add in the UI elements for routing
Calculate the toll cost of 3x different routes and then display them on the map.
Creating a base map using the Javascript v3.1
Head over to developer.here.com and log in. You can find this API credentials in the Projects section of your Developer Portal account.
Now, lets get to the business. To create a base interactive map use the following code:
Open up an editor and create an HTML file.
Add the following code and save the file.
<html>
<head>
<meta name="viewport" content="initial-scale=1.0, width=device-width" />
<script src="https://js.api.here.com/v3/3.1/mapsjs-core.js"
type="text/javascript" charset="utf-8"></script>
<script src="https://js.api.here.com/v3/3.1/mapsjs-service.js"
type="text/javascript" charset="utf-8"></script>
<!--The following libs will make the map interactive -->
<script type="text/javascript" charset="UTF-8" src="https://js.api.here.com/v3/3.1/mapsjs-mapevents.js"></script>
<script type="text/javascript" charset="UTF-8" src="https://js.api.here.com/v3/3.1/mapsjs-ui.js"></script>
<script type="text/javascript" charset="UTF-8" src="https://js.api.here.com/v3/3.1/mapsjs-clustering.js"></script>
<script type="text/javascript" charset="UTF-8" src="https://js.api.here.com/v3/3.1/mapsjs-data.js"></script>
<link rel="stylesheet" type="text/css" href="https://js.api.here.com/v3/3.1/mapsjs-ui.css" />
</head>
<body>
<!-- Here is div in which the map will render -->
<div style="width: 100vh; height: 100vw" id="mapContainer"></div>
<script>
// Initialize the platform object:
var platform = new H.service.Platform({
'apikey': '{YOUR_APIKEY}' // get the apikey from developer.here.com
});
// Obtain the default map types from the platform object
var maptypes = platform.createDefaultLayers();
// Instantiate (and display) a map object:
var map = new H.Map(
document.getElementById('mapContainer'),
maptypes.vector.normal.map,
{
zoom: 8,
center: { lat:40.730610,lng:-73.935242 }
});
</script>
</body>
</html>
If you are interested, take a look at the step-by-step guide on how to create an interactive map using the HERE Javascript API Quick Start Guide.
Open the created file using the browser, so far the base map should look like this:
alt text
We are done with basic map and it is looking good isn’t it?
Note: Don’t forget to add your apikey to the above code.
In the above code we are importing required the HERE libraries, code to render the map centered on New York city.
Add in the UI elements for routing
Lets keep going with adding UI elements(For this I’m using popular UI lib Bootstrap). We want to have two input fields, two drop downs and a submit as shown below and from now onwards lets call it “ctrl-panel”.
alt text
You can add the below code inside the <body> tag of the your html page.
Lets add some CSS to align the ctrl-panel to the top left corner on the map. It’s always good create separate file for the CSS, lets create a CSS file and add the following code.
Now that we have added the control panel to the map, the map should look like this:
alt text
Calculate the toll cost of 3x different routes
It’s time to add the functionality for the control-panel. We will implement it so that it will calculate the routes from Point A to Point B along with toll cost for each route. For this we will be using the HERE Fleet Telematics REST API. You can learn more about this API at Fleet Telematics/Toll Cost calculation
The following code will help us to calculate the toll cost from the source to the destination. The API requires the source, destination, vehicle details and a callback function.
var calculateRoute = function (start, destination) {
feedbackTxt.innerHTML = ''
// generate routing request
var transportMode = "car";
if (vehicles.value == "3" || vehicles.value == "9") {
transportMode = "truck"
}
if (vehicles.value == "9" && serverURL.value.search("fleet") != -1 ) {
transportMode = "delivery"
}
var hasTrailer = null, shippedHazardousGoods = null, limitedWeight = null,
trailerWeight =null, height = null, width = null, length = null,
heightAbove1stAxle = null;
if (parseInt(trailerType.value) > 0) {
hasTrailer = "&trailersCount=1";
}
if (parseInt(hazardousType.value) == 1) {
shippedHazardousGoods = "&shippedHazardousGoods=explosive";
}
else if (parseInt(hazardousType.value) == 2) {
shippedHazardousGoods = "&shippedHazardousGoods=other";
}
if (parseInt(vehWeight.value) > 0) {
if (parseInt(vehWeight.value) > parseInt(totalWeight.value)) {
alert("Total Weight cannot be smaller than Vehicle Weight");
return;
}
limitedWeight = "&limitedWeight=" + (totalWeight.value / 1000) + "t";
}
if (parseInt(vehHeight.value) > 0 || parseInt(trailerHeight.value) > 0) {
height = "&height=" + ((parseInt(vehHeight.value) > parseInt(trailerHeight.value) ? parseInt(vehHeight.value) : parseInt(trailerHeight.value)) / 100) + "m";
}
if (parseInt(totalWidth.value) > 0) {
width = "&width=" + (totalWidth.value / 100);
}
if (parseInt(totalLength.value) > 0) {
length = "&length=" + (totalLength.value / 100);
}
if(document.getElementById("heightAbove1stAxle").value != 0) {
heightAbove1stAxle = (document.getElementById("heightAbove1stAxle").value / 100) + "m";
}
var vspec = `&tollVehicleType=${vehicles.value}&trailerType=0&vehicleNumberAxles=2&trailerNumberAxles=0&hybrid=0
&emissionType=5&fuelType=petrol&trailerHeight=${trailerHeight.value}&vehicleWeight=${vehWeight.value}
&disabledEquipped=${disabledEquipped.value}&minimalPollution=minPollution.value&hov=${hov.value}
&passengersCount=${nrPassengers.value}&tiresCount=${nrOfTotalTires.value}&commercial=${commercial.value}
&heightAbove1stAxle=${heightAbove1stAxle}`;
if (width != null && width.length > 0) vspec += width;
if (length != null && length.length > 0) vspec += length;
if (shippedHazardousGoods != null && shippedHazardousGoods.length > 0) vspec += shippedHazardousGoods;
var routerParamsValue = '';
var finalParamsValue = '';
if (routerParamsValue !== '') {
var paramsArray = [];
var components = routerParamsValue.split('&');
for (var i = 0; i < components.length; i++) {
var key = components[i].split('=');
if (key[0].substr(0, 'waypoint'.length) === 'waypoint') {
continue;// ignore waypoints because we already specified.
}
if (key[0] === 'mode') {
continue;// Ignore mode since cor build this inside
}
paramsArray.push(components[i]);
}
finalParamsValue = paramsArray.join('&');
}
var routeAlternativesRequested = false;
if(document.getElementById("routeAlternatives").value != null && document.getElementById("routeAlternatives").value != "0") {
routeAlternativesRequested = true;
}
var isDTFilteringEnabled = document.getElementById("chkEnableDTFiltering").checked;
var rollupPrm = serverURL.value.search("fleet") != -1 ? "rollups" : "rollup"
// Preparing the tollcost API end with all required params
var urlRoutingReq = `https://fleet.ls.hereapi.com/2/calculateroute.json?apiKey={YOUR_API_KEY}&waypoint0=${start.lat},${start.lng}&detail=1&waypoint1=${destination.lat},${destination.lng}
&routelegattributes=li&routeattributes=gr&maneuverattributes=none&linkattributes=${'none,rt,fl'}&legattributes=${'none,li,sm'}¤cy=${document.getElementById('currency').value}&departure=
${isDTFilteringEnabled ? document.getElementById("startRouteDate").value + "T" + document.getElementById("startRouteTime").value : ''}
${vspec}&mode=fastest;${transportMode};traffic:disabled${((shippedHazardousGoods != null && shippedHazardousGoods.length > 0) ? shippedHazardousGoods : "")}
&${rollupPrm}=none,country;tollsys${(routeAlternativesRequested ? "&alternatives=" + document.getElementById("routeAlternatives").value : '')}&jsoncallback=parseRoutingResponse`
$('#mydiv').fadeIn('slow');
script = document.createElement("script");
script.src = urlRoutingReq;
document.body.appendChild(script);
}
Once the user fills in the details and clicks on the submit button, the above function will fire the API call to the HERE toll-cost API. On success it will send the response to callback function by calling it. Note: Click here to learn more about routing. Now, we need to implement the callback function. We will call it “parseRoutingResponse”. This is what we have defined as the callback function in the toll-cost API in the above function.
function parseRoutingResponse(resp) {
feedbackTxt.innerHTML = ''
if (resp.errors != undefined && resp.errors.length != 0) {
if (resp.errors[resp.errors.length-1] == "NoRouteFound") {
alert('Please consider to change your start or destination as the one you entered is not reachable with the given vehicle profile');
feedbackTxt.innerHTML = 'The Router service is unable to compute the route: try to change your start / destination point';
}
else {
alert(JSON.stringify(resp));
$('#mydiv').fadeIn('slow');
feedbackTxt.innerHTML = JSON.stringify(resp);
}
return;
}
if (resp.response == undefined) {
if (resp.subtype == "NoRouteFound") {
alert('Please consider to change your start or destination as the one you entered is not reachable with the given vehicle profile');
feedbackTxt.innerHTML = 'The Router service is unable to compute the route: try to change your start / destination point';
}
else {
alert(resp.subtype + " " + resp.details);
feedbackTxt.innerHTML = resp.error;
}
return;
}
routeLinkHashMap = new Object();
// create link objects
for (var r = 0; r < resp.response.route.length; r++) {
for (var m = 0; m < resp.response.route[r].leg[0].link.length; m++) {
var strip = new H.geo.LineString(),
shape = resp.response.route[r].leg[0].link[m].shape,
i,
l = shape.length;
for (i = 0; i < l; i += 2) {
strip.pushLatLngAlt(shape[i], shape[i + 1], 0);
}
routeColors[r] = routeColor[r];
var link = new H.map.Polyline(strip,
{
style: {
lineWidth: (routeStroke - (r + 1)), // alternatives get smaller line with
strokeColor: routeColor[r],
}
});
link.setArrows({color: "#F00F", width: 2, length: 3, frequency: 4});
link.$linkId = resp.response.route[r].leg[0].link[m].linkId;
routeLinkHashMap[(resp.response.route[r].leg[0].link[m].linkId.lastIndexOf("+", 0) === 0 ? resp.response.route[r].leg[0].link[m].linkId.substring(1) : resp.response.route[r].leg[0].link[m].linkId)] = link;
group.addObject(link);
link.addEventListener('tap',function(e){
var link = new H.map.Polyline(strip,
{
style: {
lineWidth: (routeStroke - (r + 1)), // alternatives get smaller line with
strokeColor: 'rgba(240, 255, 0, 1)',
lineCap: 'butt'
}
});
map.addObject(link);
})
}
}
map.addObject(group);
(async function(){
await sleep(2000);
map.setZoom(map.getViewModel().getLookAtData().zoom-1);
console.log('sleep')
})();
map.getViewModel().setLookAtData({bounds: group.getBoundingBox()},true);
for(var i = 0; i < resp.response.route.length; i++) {
highlightRoute(resp.response.route[i].tollCost.routeTollItems, i);
showTceCost(resp.response.route[i].tollCost.costsByCountryAndTollSystem, resp.response.route[i].cost,resp.response.route[i].summary, resp.warnings,routeIDs[i],routeColors[i]);
}
$('#mydiv').fadeOut('slow')
}
As part of the above function we need to create the following two functions:
highlightRoute
showTceCost
Once we get the response back from the toll-cost API we need plot the routes(highlightRoute) and display the toll cost information(showTceCost).
Lets define the highlightRoute like below:
function highlightRoute(routeTollItems, routeAlternative) {
if (routeTollItems != null) {
for (var i = 0; i < routeTollItems.length; i++) {
var tollType = routeTollItems[i].tollType;
var color = ppType_S_Color[routeAlternative];
if(tollType == 'A') {
color = ppType_A_Color[routeAlternative];
} else if(tollType == 'a') {
color = ppType_a_Color[routeAlternative];
} else if(tollType == 'S'){
color = ppType_S_Color[routeAlternative];
} else if(tollType == 'p'){
color = ppType_p_Color[routeAlternative];
} else if(tollType == 'F'){
color = ppType_F_Color[routeAlternative];
} else if(tollType == 'K'){
color = ppType_K_Color[routeAlternative];
} else if(tollType == 'U'){
color = ppType_U_Color[routeAlternative];
}
for (var j = 0; j < routeTollItems[i].linkIds.length; j++) {
// set color and stroke of links
var tollstroke = (tollCostStroke - (routeAlternative + 1)); // route alternatives have a different stroke
var link = routeLinkHashMap[routeTollItems[i].linkIds[j]];
if(link.getStyle().strokeColor == routeColor[routeAlternative]) { // only change link color to toll color if not already modified
link.setStyle({strokeColor: color, lineWidth: tollstroke});
}
}
//toll structures
if(routeTollItems[i].tollStructures != null) {
for (var j = 0; j < routeTollItems[i].tollStructures.length; j++) {
console.log({'routeTollItems':routeTollItems[i]})
createTollMarker(routeTollItems[i].tollStructures[j],routeTollItems[i]);
}
}
}
}
}
var createIconMarker = function (line1, line2) {
var svgMarker = svgMarkerImage_Line;
svgMarker = svgMarker.replace(/__line1__/g, line1);
svgMarker = svgMarker.replace(/__line2__/g, (line2 != undefined ? line2 : ""));
svgMarker = svgMarker.replace(/__width__/g, (line2 != undefined ? line2.length * 4 + 20 : (line1.length * 4 + 80)));
svgMarker = svgMarker.replace(/__widthAll__/g, (line2 != undefined ? line2.length * 4 + 80 : (line1.length * 4 + 150)));
return new H.map.Icon(svgMarker, {
anchor: new H.math.Point(24, 57)
});
};
By completing this tutorial, you should now have a functional toll-cost map built with HERE Maps API JavaScript 3.1! With this map you can compare and contrast different routes based on the time it will take to drive as well as the overall toll cost of the trip.
An extension challenge for you is to implement this map while also calculating the total Fuel cost along the way. Hint check out the Routing API to learn more.
Takeaways:
Today we created an interactive map with controls and events.
We integrated the Fleet Telematics Toll Cost API to display the toll cost.