The first thing we need is a way for our user to define a starting point, ending point and ask for directions. Although we could build a form, a floating context menu seems more appropriate.
The Menu HTML
The first step is to build an HTML menu and add it to the body:
<div id="menu">
<ul id="popupmenu" class="pmenu">
<li><a href="#" onclick='SetStart()'>Set Start</a></li>
<li><a href="#" onclick='SetEnd()'>Set End</a></li>
<li><a href="#" onclick='GetDirections()'>Get Directions</a></li>
</ul>
</div>
Listing 2 The Menu
Menu StyleSheet
Next, we need to add the styles that will define the menu. Put this block of style code in the <head> section of Listing 1:
<style type="text/css" media="screen">
ul, li {margin:0;padding:0;}
ul.pmenu
{
position:absolute;
margin: 0;
padding: 1px;
list-style: none;
width: 150px; /* Width of Menu Items */
border: 1px solid #ccc;
background:white;
display:none;
z-index:10;
}
ul.pmenu li { position: relative; }
/* Styles for Menu Items */
ul.pmenu li a
{
display: block;
text-decoration: none;
color: black;
padding: 2px 5px 2px 20px;
}
ul.pmenu li a:hover
{
background:#335EA8;
color:white;
}
</style>
Listing 3 The Menu CSS
Grabbing the ContextMenu Event
Last, we need to figure out a way to make our menu appear and disappear. Since we only want the menu to appear when the user right-clicks on the map area, we can create a JavaScript method to show the map and attach it to the 'onconextmenu' event of our map. Add the following line to the end of the OnPageLoad() method:
map.AttachEvent("oncontextmenu", ShowPopupMenu);
Listing 4 Listening for the context menu event.
Now we need to write the JavaScript to show the menu:
function ShowPopupMenu(e)
{
popuplat = e.view.LatLong.Latitude;
popuplon = e.view.LatLong.Longitude;
var latlong = map.LatLongToPixel(new VELatLong(popuplat,popuplon));
var x = map.GetLeft();
var y = map.GetTop();
var menu = document.getElementById('popupmenu');
menu.style.display='block'; //Showing the menu
menu.style.left = latlong.x + x; //Positioning the menu
menu.style.top = latlong.y + y;
}
Listing 5 Showing the Menu
Note that we actually are going to need two global variables (popuplat, popuplon), so add two definition lines at the beginning of the script block:
var popuplat;
var popuplon;
Listing 6 Global variables
These variables are going to define the cursor position for when we add our start and end points.
If you try your code now, you should see a popup menu when you right click anywhere on the map. Of course, none of the functions work and you can't close the menu. Our next task will be to create shells for all three methods and figure out how to close the menu.
Shell Methods for Start, End and Directions
Unfortunately, as soon as the menu opens, the VEMap control has no way of knowing when additional mouse events occur. That is, we can't simply capture a "click" event and close the map. Instead, we have to perform some "bad programming" and manually close the menu at the end of each of our functions. Add the following hollow implementations to your code:
function RemovePopupMenu()
{
var menu = document.getElementById('popupmenu').style.display='none';
}
function SetStart()
{
RemovePopupMenu();
}
function SetEnd()
{
RemovePopupMenu();
}
function GetDirections()
{
RemovePopupMenu();
}
Listing 7 Shell functions
Note that every one of our menu functions has to call the RemovePopupMenu() function in order to close the menu.
At this point, you should have a Bing Maps map, with a popup menu that will close if you select any option.
Setting the Start and End
Now, before we can actually request driving directions, we need to set our start point and end point. Our sequence of events will be:
- Remove the existing start or end pin (if there is one).
- Create a pushpin for the start (or end).
- Store the latitude and longitude in a VELatLong object.
- Close the popup menu.
First, we want to add two more global variables:
var startpt = null;
var endpt = null;
Listing 8 More global variables
Next, we want to create the pushpin in our SetStart method:
function SetStart()
{
try {
map.DeletePushpin('start');
} catch(err) {}
startpt = new VELatLong(popuplat, popuplon);
var pin = new VEPushpin('start', startpt, null, 'Start Here', 'Starting point');
map.AddPushpin(pin);
RemovePopupMenu();
}
Listing 9 The SetStart method
Note that the DeletePushpin() method throws an exception if the pushpin doesn't exist. Since we also get an exception if we try to add a new pushpin with the same ID as an existing pushpin, we have to perform a workaround. Basically, we try to delete the pushpin. If it doesn't exist, we catch the exception, but continue anyway since the exception doesn't break our functionality. Also, if we wanted to use a custom pushpin, we could replace the null third parameter with a URL to an image.
Our SetEnd method is almost identical:
function SetEnd()
{
try {
map.DeletePushpin('end');
} catch(err) {}
endpt = new VELatLong(popuplat, popuplon);
var pin = new VEPushpin('end', endpt, null, 'pin', 'end');
map.AddPushpin(pin);
RemovePopupMenu();
}
Listing 10 The SetEnd method
Our application should now show our popup menu and allow us to create pushpins for the starting and ending points of our route.
Driving Directions
The real magic of driving directions consists of a single call to the VEMap object. All we have to do is pass our start point, end point, and a few other parameters into the GetRoute method, and we will have a nice route automatically drawn on our map.
function GetDirections()
{
map.GetRoute(startpt, endpt, VEDistanceUnit.Kilometers, VERouteType.Quickest);
RemovePopupMenu();
}
Listing 11 Getting Directions
Note that we are reusing the endpoints created by the start and end methods. We're also setting all of our directions so that they will display with Kilometers (remember, we're looking at a map of Sydney!), and we are going to use the quickest route rather than the shortest route.
Of course, we may want to display the text directions for our users. Although we would most likely want to create a custom panel, in this example, we will simply print the directions to an alert popup message. All we have to do is slightly modify our GetRoute() call to pass in a callback function:
function GetDirections()
{
map.GetRoute(startpt, endpt, VEDistanceUnit.Kilometers, VERouteType.Quickest,
OnGotRoute);
RemovePopupMenu();
}
Listing 12 Modified GetDirections method
Now we need to create our OnGotRoute function, which will process the array of driving directions returned by the GetRoute() call.
function OnGotRoute(route)
{
var routeinfo="Route info:\n\n";
routeinfo+="Total distance: ";
routeinfo+= route.Itinerary.Distance+" ";
routeinfo+= route.Itinerary.DistanceUnit+"\n";
var steps="";
var len = route.Itinerary.Segments.length;
for(var i = 0; i < len ;i++)
{
steps+=route.Itinerary.Segments[i].Instruction+" -- (";
steps+=route.Itinerary.Segments[i].Distance+") ";
steps+=route.Itinerary.DistanceUnit+"\n";
}
routeinfo+="Steps:\n"+steps;
alert(routeinfo);
}
Listing 13 The OnGotRoute callback
Now, whenever our users click on Get Directions (after setting a start and end), they will not only see the directions overlaid on the map, but they will also see the written directions in a popup window.
Conclusion
Although the base VEMap control gives you easy access to driving directions, you may find circumstances where you need greater control, or want to perform more complex routing. Fortunately, you can leverage MapPoint Web Service (the other part of the Bing Maps for Enterprise) and integrate the returned routes into your Bing Maps display. If you need more complex routing solutions, please examine MapPoint Web Service.
This article is an update of an article originally contributed by Dr Neil Roodyn. The update was performed by Robert McGovern MVP (Bing Maps/MapPoint).
The final source for the complete example is as follows:
<html>
<head>
<title>Driving Directions in Bing Maps</title>
<style type="text/css" media="screen">
ul, li {margin:0;padding:0;}
ul.pmenu
{
position:absolute;
margin: 0;
padding: 1px;
list-style: none;
width: 150px; /* Width of Menu Items */
border: 1px solid #ccc;
background:white;
display:none;
z-index:10;
}
ul.pmenu li { position: relative; }
/* Styles for Menu Items */
ul.pmenu li a
{
display: block;
text-decoration: none;
color: black;
padding: 2px 5px 2px 20px;
}
ul.pmenu li a:hover
{
background:#335EA8;
color:white;
}
</style>
<script src="http://dev.virtualearth.net/mapcontrol/v4/mapcontrol.js"></script>
<script>
var map = null;
var popuplat;
var popuplon;
var startpt = null;
var endpt = null;
function OnPageLoad()
{
map = new VEMap('myMap');
map.LoadMap(new VELatLong(-33.7939, 151.1093), 10, 'r', false);
map.SetScaleBarDistanceUnit(VEDistanceUnit.Kilometers);
map.AttachEvent("oncontextmenu", ShowPopupMenu);
}
function ShowPopupMenu(e)
{
popuplat = e.view.LatLong.Latitude;
popuplon = e.view.LatLong.Longitude;
var latlong = map.LatLongToPixel(new VELatLong(popuplat,popuplon));
var x = map.GetLeft();
var y = map.GetTop();
var menu = document.getElementById('popupmenu');
menu.style.display='block'; //Showing the menu
menu.style.left = latlong.x + x; //Positioning the menu
menu.style.top = latlong.y + y;
}
function RemovePopupMenu()
{
var menu = document.getElementById('popupmenu').style.display='none';
}
function SetStart()
{
try {
map.DeletePushpin('start');
} catch(err) {}
startpt = new VELatLong(popuplat, popuplon);
var pin = new VEPushpin('start', startpt, null, 'Start Here', 'Starting point');
map.AddPushpin(pin);
RemovePopupMenu();
}
function SetEnd()
{
try {
map.DeletePushpin('end');
} catch(err) {
endpt = new VELatLong(popuplat, popuplon);
var pin = new VEPushpin('end', endpt, null, 'pin', 'end');
map.AddPushpin(pin);
RemovePopupMenu();
}
function GetDirections()
{
map.GetRoute(startpt, endpt, VEDistanceUnit.Kilometers, VERouteType.Quickest, OnGotRoute);
RemovePopupMenu();
}
function OnGotRoute(route)
{
var routeinfo="Route info:\n\n";
routeinfo+="Total distance: ";
routeinfo+= route.Itinerary.Distance+" ";
routeinfo+= route.Itinerary.DistanceUnit+"\n";
var steps="";
var len = route.Itinerary.Segments.length;
for(var i = 0; i < len ;i++)
{
steps+=route.Itinerary.Segments[i].Instruction+" -- (";
steps+=route.Itinerary.Segments[i].Distance+") ";
steps+=route.Itinerary.DistanceUnit+"\n";
}
routeinfo+="Steps:\n"+steps;
alert(routeinfo);
}
</script>
</head>
<body onload="OnPageLoad();">
<div id="myMap" style="position:relative;width:600px;height:400px;"></div>
<div id="menu">
<ul id="popupmenu" class="pmenu">
<li><a href="#" onclick='SetStart()'>Set Start</a></li>
<li><a href="#" onclick='SetEnd()'>Set End</a></li>
<li><a href="#" onclick='GetDirections()'>Get Directions</a></li>
</ul>
</div>
</body>
</html>
Listing 14 Complete Listing for Driving.html