Working with the Virtual Earth Map Control

The Virtual Earth map control is the power behind Windows Live Local. Using the Virtual Earth 3.1 map control (VE) APIs, you too can create some amazing location based applications of your own. I've worked with VE on a few projects now and have a new tips that I thought I'd pass along. Some of these can be found scattered on the Internet, while others I figured out on my own. In any case, here are some of tips that may help you on your next Virtual Earth project. A word of caution: some of the ideas presented here are undocumented and therefore could change in future versions of the VE API.

Route not showing up?

If you are using map.GetRoute() and are having problems with the route showing up on the map, make sure your web page is encoded with UTF-8. This can be done by adding the a meta tag to the HTML in the <head> section, as show here:

<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />

You can also set the encoding in ASP.NET like this:

Response.ContentType = "text/html";
Response.ContentEncoding = Encoding.UTF8;

Roll your own alerts messages

Some of the VE APIs will cause VE to display a visual error message on the screen. Your application may not want this as VE's default formatting (to some) may leave something to be desired. Here is an example of what is displayed on the map when you use the API function Find() to locate "movie theater".

movietheatre Developer

As you can see, it is asking if we really meant "movie theatre" (note the difference in spelling). While this may be a perfectly acceptable thing to do if the search query came from user input, ours came from hard coding, so of course we meant "movie theater". We don't want to bother the user with unneeded visuals.

Well, lucky for us, there is an undocumented public method of the map control that is called when VE displays an error message. It's called ShowMessage(txt). As you can see in the code below, we can easily override this method to do something else. In this case we simple take the passed parameter (i.e. the error message) and display it within a div element.

When you run the code, click on the button on the left to see what happens natively (i.e. you will get the built-in error message popup). Clicking the button on the right will override the built-in ShowMessage and instruct VE to call our code instead (which simply ignores the error message). We put things back to normal when we're done.

The complete code is shown below. You can run the sample here (or right click and select "Save Target As…" to get the source code).

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head>
    <title>My VE Demo</title>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <style type="text/css">
        body, html, .map {
            position:relative;
            width:100%;
            height:100%;
            margin:0;
            overflow:hidden;
        }
        .plotControl {
            position:absolute;
            right:5px;
            top:5px;
            width:220px;
            padding:5px;
            border:solid 1px #cccccc;
            background-color:white;
            filter:alpha(opacity=90);
            -moz-opacity:0.9;
            opacity:0.9;
        }
    </style>
    <script type="text/javascript" src="http://dev.virtualearth.net/mapcontrol/v3/mapcontrol.js"></script>
    <script type="text/javascript">
        var vemap; //our map object
        var saveShowMessage; //pointer to the original ShowMessage function

        function onLoad() {
            vemap = new VEMap("map"); //create the map
            vemap.LoadMap();
            saveShowMessage=vemap.ShowMessage; //save original function pointer

            var plotControlEl = document.getElementById("plotControl"); //add our custom control
            vemap.AddControl(plotControlEl);
            plotControlEl.style.top = "";
            plotControlEl.style.left = "";
        }
        function ourShowMesage(txt) {} //do nothing

        function plotDefault() {
            vemap.Find("movie theater", "", 1);
        }
        function plotNoAlert() {
            vemap.ShowMessage=ourShowMesage; //redirect alert messages to our code
            vemap.Find("movie theater", "", 1, callback);
            function callback() {
                vemap.ShowMessage=saveShowMessage; //restore original value
            }
        }
        window.onload=onLoad;
    </script>
</head>
<body>
    <div class="map" id="map"></div>
    <div class="plotControl" id="plotControl">
        Plot Theaters:
        <button onclick="plotDefault();">Default</button>
        <button onclick="plotNoAlert();">No Alert</button>
    </div>
</body>
</html>

Custom Control positioning with CSS

Like many of you, I prefer to leave much of my formatting up to CSS. But when I added my first custom control to a VE map using AddControl(), I found that the CSS positioning that I specified was being overridden by VE and set to 0,0. After a while, I determined that VE was setting the position of the element itself to style.left="0" and style.top="0". So all you have to do is clear these values after adding the custom control and the values you have in the style sheet will dictate the custom control's position as you would expect.

var plotControlEl = document.getElementById("plotControl"); //add our custom control
vemap.AddControl(plotControlEl);
plotControlEl.style.top = "";
plotControlEl.style.left = "";

Longitude and Latitude encoding

Another little know VE class is VELatLongEncoding. It contains 2 methods, Encode and Decode. Encode takes 2 floating point numbers representing a coordinate latitude and longitude and returns a encoded string 12 characters long. Decode takes an encoded string and returns an array with 2 elements. The first element is a float represents latitude and the second longitude.

Below is a little "do nothing" code that retrieves the current map position, encodes it into a string, decodes it and repositions the map. You will notice some rounding error during the encoding/decoding process if you use a debugger to step through the code.

var enc = new VELatLongEncoding(); //create an instance of the Encoding class
var latlong = vemap.GetCenter(); //get the current map center
var str = enc.Encode(latlong.Latitude, latlong.Longitude); //encode it into a string
var ll_array=enc.Decode(str); //decode the string into an array
vemap.SetCenter(new VELatLong(ll_array[0], ll_array[1])); //reposition the map (to the same place)

I'm going to leave the "why" behind the need to encode/decode a latitude/longitude up to you. I'm sure some of you will find this useful.

Conclusion

There are many resources available for working with Virtual Earth, but the best place to start my be Virtual Earth's home on dev.live.com. There you will find the Virtual Earth Interactive SDK which is a fun way to learn how to use the Microsoft Virtual Earth map control APIs. The Interactive SDK shows you how the map control works and provides the complete code you need to implement the task on your own page.

Also on dev.live.com you will find sample mashups that use the Virtual Earth map control, along with articles explaining how they were written

  • Show Your Contacts on a Map – This demo uses the Windows Live Contacts control and Virtual Earth to (yes, you guessed it) show your contacts on a map.  see article | run demo
  • Start a Party – Takes this approach one a step further by using the Virtual Earth's Find feature to locate restaurants, bars, bowling alleys etc and emails personalized driving directions to your friends.   see article | run demo
  • Crash a Party – Yet another step further by adding events (such as those from eventful.com) and Virtual Earth's "collections".   see article | run demo

You will find that Virtual Earth is relatively simple to start using and, as you dive in deeper, very powerful. Hopefully some of these tips will help you write better Virtual Earth web applications.