Navigation¶
This complete example displays a movable and zoomable map,
embedded inside an html element on a webpage,
computes and renders a route and simulates navigation along the route.
Overview¶
A desired route is defined using at least a given starting point and a
given destination, both specified using (longitude, latitude) coordinate pairs.
Optionally, additional waypoints can be added between the starting and
destination points, to more precisely determine the desired resulting route.
If the map is moved during the simulated navigation,
the camera will no longer follow the green arrow.
To start following again after panning the map,
click the blue arrow icon.
The map supports pan and zoom, and is fully 3D,
so holding down the shift key and panning will
simultaneously rotate and tilt the map.
If you want to jump right in, you can download the HTML file and the JavaScript file for this example in the same directory, and then load the HTML file in a web browser to try it out right now!
Or you can continue reading for a detailed explanation of the code.
How it works¶
1// Start by setting your token from https://developer.magiclane.com/api/projects 2gem.core.App.token="your_API_key_token";The first step in JavaScript is to set your API key tokengem.core.App.token
, which you can get at the Magic Lane website, see the Getting Started tutorial.You only need to type your email address and create a new password.1function activateLocation() 2{ 3 // View of the map 4 var defaultView = gem.core.App.getDefaultScreen().getDefaultMapView(); 5 // Set the camera to follow the simulated position of the green arrow along the route on the map 6 // when the blue arrow icon is clicked; panning or clicking on the map during the simulation 7 // turns off following position, and here it is turned back on upon clicking the blue arrow icon 8 defaultView.startFollowingPosition(); 9} 10 11// Draw the next turn symbol to the left of the instructions in the top panel 12function setCanvas(image_data, width, height, canvas_name) 13{ 14 var canvas = document.getElementById(canvas_name); 15 if (canvas) 16 { 17 var imageData = new ImageData(new Uint8ClampedArray(image_data), width, height); 18 canvas.width = width; 19 canvas.height = height; 20 canvas.getContext('2d').putImageData(imageData, 0, 0); 21 } 22 else 23 { 24 console.error('Canvas name not valid'); 25 } 26}The function to resume following the green arrow after panning the map is defined at line 4.The function to draw the next turn symbol is defined at line 15.1// The main function 2gem.core.App.registerInitialCallFunction(function() 3{ 4 // View of the map 5 var defaultView = gem.core.App.getDefaultScreen().getDefaultMapView(); 6 7 // Sample departure point and destination coordinates for a route 8 var departureCoordinates = {latitude: 37.7749,longitude: -122.4194, altitude: 0, bearing: 0.0}; 9 var destinationCoordinates = {latitude: 38.5816,longitude: -121.4944, altitude: 0, bearing: 0.0}; 10 11 var defaultRoute = new gem.routesAndNavigation.RoutesRequest(); 12 defaultRoute.addWaypoint(departureCoordinates); 13 defaultRoute.addWaypoint(destinationCoordinates); 14 defaultRoute.setBuildTerrainProfile(false); 15 16 // This function is called when calculateRoute completes 17 var callbackFunction = function() 18 { 19 var msecFlightDuration = 3000; 20 21 // Draw the computed route on the map 22 defaultView.showRouteInView(defaultRoute.get(0),true); 23 24 // Fly the camera to the result route bounding box/area, with flight duration in milliseconds 25 defaultView.centerOnRoute(defaultRoute.get(0),msecFlightDuration); 26 27 var navigationInstructionEM = new gem.routesAndNavigation.NavigationListener(); 28 // Function to write navigation instructions and statistics in their respective panels on the map 29 var navInstructionUpdate = function() 30 { 31 // Image of the next turn displayed to the left of the instructions 32 var bitmapContainer = new gem.core.BitmapContainer(30,30); 33 gem.routesAndNavigation.Navigation.getNextTurnImageInBitmap(bitmapContainer); 34 setCanvas(bitmapContainer.toImageData(),30,30,"myCanvas"); 35 var distancetoNext = 36 gem.routesAndNavigation.Navigation.getTimeDistanceToNextTurn().totaldistance; 37 var unit=" meters " 38 if (distancetoNext>1000) 39 { 40 distancetoNext = distancetoNext/1000.0; 41 unit = " Km "; 42 } 43 // Update the instructions, time and distance remaining, in the top and bottom panels 44 document.getElementById('navigationText').innerHTML = "<p> In " + distancetoNext.toFixed(2) 45 + unit + gem.routesAndNavigation.Navigation.getNextTurnInstruction() + "</p>"; 46 var remainedTimeDistance = gem.routesAndNavigation.Navigation.getRemainingTravelTimeDistance(); 47 document.getElementById('bottomPanelRemainingDistance').innerHTML 48 = (remainedTimeDistance.totaldistance/1000).toFixed(2) + "km"; 49 var measuredTime = new Date(null); 50 measuredTime.setSeconds(remainedTimeDistance.totaltime); // specify value in seconds 51 var MHSTime = measuredTime.toISOString().substr(11, 5); 52 document.getElementById('bottomPanelRemainingTime').innerHTML = MHSTime+"hr"; 53 bitmapContainer.delete(); 54 }; 55 navigationInstructionEM.registerNavInstructionUpdateListener(navInstructionUpdate); 56 var progressListener = function() 57 { 58 // Set the camera to follow the simulated position of the green arrow along the route on the map 59 defaultView.startFollowingPosition(); 60 }; 61 62 // Make the navigation instructions and statistics panels visible when navigation starts 63 var navpanel = document.getElementById('navigationPanel'); 64 var bottomPanel = document.getElementById('bottomPanel'); 65 66 // Start simulated navigation after the fly to the route is complete 67 setTimeout(function() 68 { 69 navpanel.style.visibility='visible'; 70 bottomPanel.style.visibility='visible'; 71 gem.routesAndNavigation.Navigation.startSimulation(defaultRoute.get(0),progressListener, 72 navigationInstructionEM); 73 },msecFlightDuration); 74 75 }; 76 77 // Compute the defaultRoute from the starting point to the destination initialized above 78 defaultRoute.calculateRoute(callbackFunction); 79}); 80 81// Initializes the app 82gem.core.App.initApp();The main function where execution starts is defined:gem.core.App.registerInitialCallFunction(function()
The default view of the map is loaded:var defaultView = gem.core.App.getDefaultScreen().getDefaultMapView();
Sample departure and destination coordinates are defined for use to compute a route and to demonstrate navigation along a route.var departureCoordinates = {latitude: 37.7749,longitude: -122.4194, altitude: 0, bearing: 0.0};
var destinationCoordinates = {latitude: 38.5816,longitude: -121.4944, altitude: 0, bearing: 0.0};
Altitude is in meters, and bearing(heading) is in degrees, where 0 is north, 90 is east, 180 is south and 270 (or -90) is west.A route request object is obtained, and the departure and destination coordinates are set in it.var defaultRoute = new gem.routesAndNavigation.RoutesRequest();
defaultRoute.addWaypoint(departureCoordinates);
defaultRoute.addWaypoint(destinationCoordinates);
Optionally, additional waypoints could be added between the departure and the destination, so the calculated route passes through those locations.The route calculation is requested. The given callback function is called when the route computation is complete and the simulation starts automatically.defaultRoute.calculateRoute(callbackFunction);
This function is called when calculateRoute completes:var callbackFunction = function()
Draw the first computed route on the map (index 0), as the route computation results in a list of routes, as there could be multiple alternate routes between the departure and destination points:defaultView.showRouteInView(defaultRoute.get(0),true);
Fly the camera to the route bounding box/area, with flight duration in milliseconds, so this is a 3 second flight. Setting this value to 0 results in jumping instantly to the route.defaultView.centerOnRoute(defaultRoute.get(0),msecFlightDuration);
A timeout is set,setTimeout(function()
so that the navigation simulation starts automatically,gem.routesAndNavigation.Navigation.startSimulation(defaultRoute.get(0)
after the flight to the first computed route in the resulting list of routes between the specified departure and destination points, is complete.The function to update the navigation instructions and statistics in their respective top and bottom panels on the map during simulated navigation is defined.var navInstructionUpdate = function()
Panning or clicking on the map during the simulated navigation along the computed route turns off following position.When the blue arrow icon is clicked, this function is called,function activateLocation()
which causes the camera to again follow the position of the green arrow as it moves along the route.1<!doctype html> 2<html> 3<meta charset="utf-8"> 4<meta name="viewport" content="width=device-width, initial-scale=1, 5 maximum-scale=1, minimum-scale=1, user-scalable=no, shrink-to-fit=no"/> 6<meta name="apple-mobile-web-app-capable" content="yes"> 7<meta name="HandheldFriendly" content="true"/> 8<title>Navigation Example - Maps SDK for JavaScript</title> 9<!-- <link rel="icon" type="image/x-icon" href="./favicon.ico"/> --> 10<!-- <link rel="shortcut icon" type="image/x-icon" href="./favicon.ico"/> --> 11<link rel="stylesheet" type="text/css" href="https://www.magiclane.com/sdk/js/gem.css"> 12<!-- <link rel="manifest" href="manifest.json" crossorigin="use-credentials"> --> 13<link rel="apple-touch-icon" href="appleicon.png" crossorigin="use-credentials"> 14<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.6.1/css/all.css" 15 integrity="sha384-gfdkjb5BdAXd+lj+gudLWI+BXq4IuLW5IT+brZEZsLFm++aCMlF1V92rMkPaX4PP" crossorigin="anonymous"> 16 17<!-- ######################################################## --> 18<!-- This is the main rendering region/canvas. --> 19<!-- ######################################################## --> 20<canvas class="emscripten" id="canvas" oncontextmenu="event.preventDefault()" tabindex=0></canvas> 21 22<!-- ######################################################## --> 23<!-- This is the loading indicator circle. --> 24<!-- ######################################################## --> 25<div id="wrapper"> 26<div class="profile-main-loader"> 27<div class="loader"> 28<svg class="circular-loader"viewBox="25 25 50 50" > 29<circle class="loader-path" cx="50" cy="50" r="20" fill="none" stroke="#70c542" stroke-width="2"/> 30</svg> 31</div> 32</div> 33</div> 34 35<!-- ######################################################## --> 36<!-- These are the display panels for the Navigation example. --> 37<!-- ######################################################## --> 38<style> 39body {overflow: hidden;margin: 0; background-color: white} 40.navigationPanel 41{ 42 position: fixed; 43 top: 0%; 44 left: 50%; 45 width: 256px; 46 height: auto; 47 margin-left: -128px; 48 background-color: rgba(0, 0, 0, 0.5); 49 color: white; 50 visibility: hidden; 51} 52.navigationText 53{ 54 position: relative; 55 min-height: 30px; 56 width:220px; 57 color: white; 58 font-size: 25px; 59 font-family: monospace, monospace; 60 left: 20px; 61} 62.navigationText p 63{ 64 color: white; 65 text-decoration: none; 66 display: block; 67 text-align: center; 68} 69.bottomPanel 70{ 71 position: fixed; 72 bottom: 0%; 73 left: 50%; 74 width: 256px; 75 height: 30px; 76 margin-left: -128px; 77 background-color: rgba(0, 0, 0, 0.5); 78 color: white; 79 visibility: hidden; 80} 81.bottomPanelRemainingDistance 82{ 83 position: absolute; 84 color: white; 85 font-size: 25px; 86 font-family: monospace, monospace; 87 right: 5px; 88 text-align: center; 89} 90.bottomPanelRemainingDistance p 91{ 92 color: white; 93 text-decoration: none; 94 display: block; 95 text-align: center; 96} 97.bottomPanelRemainingTime 98{ 99 position: absolute; 100 color: red; 101 font-size: 25px; 102 font-family: monospace, monospace; 103 left: 5px; 104 text-align: center; 105} 106.bottomPanelRemainingTime p 107{ 108 color: red; 109 text-decoration: none; 110 display: block; 111 text-align: center; 112} 113.btn 114{ 115 width: 20px; 116 height: 20px; 117 background: #FFF; 118 border: 1px solid #005bac; 119 color: #005bac; 120 padding: 10px 10px 5px; 121 position: fixed; 122 text-align: center; 123 -ms-user-select: none; 124 -moz-user-select: -moz-none; 125 -khtml-user-select: none; 126 -webkit-user-select: none; 127 user-select: none; 128 transition: all 0.3s ease; 129 z-index: 10 130} 131.btn:hover 132{ 133 background: #eef; 134} 135.location 136{ 137 bottom: 25px; 138 opacity: 1.0; 139 position: absolute; 140 right: 0; 141} 142</style> 143 144<!-- ######################################################## --> 145<!-- <div>s must be created before loading the scripts. --> 146<!-- ######################################################## --> 147<div class="bottomPanel" id="bottomPanel"> 148<div class="bottomPanelRemainingDistance" id ="bottomPanelRemainingDistance"> 149</div> 150<div class="bottomPanelRemainingTime" id="bottomPanelRemainingTime"/> 151</div> 152<div class="navigationPanel" id="navigationPanel"> 153<div class="navigationText" id="navigationText"> 154</div> 155<canvas id="myCanvas" class="canvasins" width="30" height="30"/> 156</div> 157<a onclick="activateLocation()" class="btn location"><i class="fas fa-location-arrow"></i></a> 158 159<!-- ######################################################## --> 160<!-- Loading the gemsdk.js GEM SDK script. --> 161<!-- ######################################################## --> 162<script type="text/javascript" src="https://www.magiclane.com/sdk/js/gemapi.js"></script> 163 164<script type="text/javascript" src="jsHelloNavigation.js"></script> 165</html>Thecanvas
is the drawing area where the map is rendered. The canvas is configured to fill the browser window.A smaller canvas, with id myCanvas, is also defined to draw the next turn symbol.Note that thediv
elements must be defined before loading the JavaScript source.The circular-loader is the rotating circle animation seen while loading.The instruction and statistics panels and the icon to return to follow position mode after panning during navigation, are defined in the <style></style> section.Once defined, the panels are put inside div elements so they can be easily referenced from the JavaScript code.Clicking the blue arrow icon triggers theonclick="activateLocation()
function in JavaScript to start following the green arrow again after panning the map manually, which interrupts the camera from following the green arrow during a navigation simulation along a computed route.At the bottom, gemapi.js, the Maps SDK for JavaScript is loaded.Next, the JavaScript source of this example is loaded.
The example is 2 plain text files, one with the
HTML code (
.html
file extension) and the other with the
JavaScript code (.js
file extension).To run the example, the HTML file is loaded in a browser.
The
.js
file should be in the same directory, as it will be
loaded automatically.Source code for this example:
HTML and JavaScriptright-click on the links and select Save As.