Ricky's profileRicky's Bing Maps BlogBlogSkyDrive Tools Help

Blog


    4/16/2009

    Virtual Earth Image Viewer

    Virtual Earth is a great tool for viewing geo-spatial information on a map. It offers great user experience by being able to pan and zoom the map. Sometimes you may wish you could do this with a static image of your own. In Silverlight you can use DeepZoom to do this but doing this in a regular HTML web page requires a lot of custom JavaScript. So why not use Virtual Earth to do this for you? There are a couple of ways to do this. One is to us MapCruncher and generate a bunch of tile images and then add them to the map as a tile layer. This often results in a large number of tiles. This will work in both 2D and 3D modes which is great if your viewing geo-spatial imagery but if you only need 2D tools there are other options.

    Another option is to add the image to the map like a pushpin and to resize it depending on the zoom level. Recently Chris Pendleton blogged about an interesting way to add pushpins to the map by creating image tags and adding them to a div and then adding the div to the map as a shape layer. You can view the post here: http://blogs.msdn.com/virtualearth/archive/2009/04/09/virtual-earth-api-release-information-april-2009.aspx

    By expanding upon the methods described in this post we can view an image the same way we view a Virtual Earth map. Here is some sample code that demonstrates how to do this:

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html>
    <head>
    <title></title>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <script type="text/javascript" src="http://dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=6.2"></script>
    <script type="text/javascript">

    var map = null;
    var myLayer = null;
    var pinImage = 'http://www.okanagan.com/ski/sun_peaks_map.jpg';

    //image dimensions used form scaling
    var imageHeight = 904;
    var imageWidth = 1025;

    var previousZoomLevel = null;

    //RedrawShapes
    function RedrawShapes()
    {
        var pixel = map.LatLongToPixel(new VELatLong(85,-180));
        var zoom = map.GetZoomLevel();
        var mapWidth = 256 * Math.pow(2,zoom);
        var mapHeight = mapWidth * imageHeight/imageWidth;
        myLayer.innerHTML = "<img src='" + pinImage + "' style='position:absolute; left:"
    + pixel.x + "px; top:" + pixel.y + "px;width:"+mapWidth+"px;height:"+mapHeight+"px;' +/>";
    }

    //View Change Event handler
    function EventViewChange()
    {
        //if zoom level changed, then redraw the shapes, otherwise do nothing
        //-- the map will position the pins on pan properly
        var currentZoomLevel = map.GetZoomLevel();
        if (previousZoomLevel != currentZoomLevel)
        {
            previousZoomLevel = currentZoomLevel;
            ClearShapes();
            RedrawShapes();
        }
    }

    //Event to respond to Birdseye changes
    function EventBirdseyeChanged()
    {
        //When Birdseye rotation changes, redraw the shapes
        ClearShapes();
        RedrawShapes();
    }

    //Clear the shapes
    function ClearShapes()
    {
        myLayer.innerHTML = '';
    }

    //Start Zoom event handler
    function EventStartZoom()
    {
        //When zoom is staretd clear the shapes before zoom since they will need to be redrawn
        ClearShapes();
    }

    //Load Map Event Handler
    function EventMapLoaded()
    {
        //Store previous zoom level as current zoom level
        previousZoomLevel = map.GetZoomLevel();

        CreateLayer();

        //Register for events to update the custom layer
        map.AttachEvent("onchangeview", EventViewChange);
        map.AttachEvent("onstartzoom", EventStartZoom);
        map.AttachEvent("onobliquechange", EventBirdseyeChanged);
        RedrawShapes();
    }
    function CreateLayer()
    {
        //Create the custom layer for the map dynamically on the map surface
        myLayer = document.createElement('div');
        myLayer.style.position = "absolute";
        myLayer.style.top = "0px";
        myLayer.style.left = "0px";
        myLayer.style.width = "500px";
        myLayer.style.height = "400px";
        myLayer.style.zIndex = 1000;
        map.AddCustomLayer(myLayer);
    }

    function CreateMap()
    {
        var mapOptions = new VEMapOptions();
        mapOptions.LoadBaseTiles = false;
        map = new VEMap('myMap');
        map.onLoadMap = EventMapLoaded;
        map.LoadMap(null,1,null,null,null,null,null,mapOptions);
        map.HideDashboard();
        map.HideScalebar();
    }
    </script>
    </head>
    <body onload="CreateMap();">
    <div id='myMap' style="position:relative; width:500px; height:400px;"></div>
    </body>
    </html>

    4/10/2009

    Bird's eye Routes

    It was recently brought to my attention that routes are not supported in the Bird's eye map view. Some how this slipped by me as I swear I've seen it working before. After some thought I suspect that this has to do with possible accuracy issues. A few releases ago a new map method was introduced called VEMap.SetShapeAccuracy (http://msdn.microsoft.com/en-us/library/bb877873.aspx). This method only increases the accuracy of pushpin's. Currently if you draw a route and try an view it in the bird's eye map view you will find that there is no route line drawn only segment markers. One way to correct this issue is to retrieve the route geometry (requires a client token) and draw a polyline in place of the route line. Using this method will not increase the accuracy but will allow you to see a route line. In my tests I haven't noticed any real accuracy issues using this method. Complete source code that demonstrates this method can be found here: http://cid-e7dba9a4bfd458c5.skydrive.live.com/self.aspx/VE%20Sample%20code/BirdseyeRouting.zip

    Here is a screen shot of a bird's map with a route drawn on it using this method:

    image

    4/5/2009

    VE 3D Flight Simulator Version 1.1

    After having such a huge positive response from many people about the Virtual Earth 3D flight simulator I built a couple of weeks ago I decided to make some enhancements to it. The enhancements were mainly focused around improved controls for a XBox controller. I have overridden the default XBox controls that are, by default, similar to the controls for Halo and made them closer to the controls of other common flight simulators. The following controls where overridden:

    Left thumb control - Y direction:  This control used to only move you in a plane parallel to the ground and did not take the pitch of the camera into consideration. This control now allows you to accelerate in the direction the camera is pointing.

    Left thumb control - X direction: This control used to allow you to strafe to the side. This control now allows you to roll the plane.

    Right thumb control - Y direction: This control handles the pitch of the plane.

    Right thumb control - X direction: This control handles turning.

    Here is a video of the new simulator in action:  

     

    Additional enhancement was to hide the default location data that appeared in the bottom right corner of the screen. This was done by adding the following code into the Activate method of the flight simulator plug-in:

    this.Host.WorldEngine.ShowLocation = false;

     

    The new controls were defined by creating xml that could be added to the built in bindings. There are two ways to get this xml into your plug-in. One method is to create an xml file that needs to be merged with the default bindings xml file that is on the users computer. This requires the dll's of the plug-in to be installed onto the users computer and the dll to be placed in the GAC in order to get the required permissions to access the default bindings file. By using this method you can make it so that your controls are available in all instances of Virtual Earth 3D. An example of this method can be found here: http://blogs.msdn.com/virtualearth3d/archive/2008/05/01/installing-plug-ins.aspx

    The second method is to store the xml as a string inside of the plug-in and then add the xml data to the bindings. This method is much simpler and reduces a lot of the overhead.  An example of this method is used in the following article: http://blogs.msdn.com/virtualearth3d/archive/2008/07/08/animation.aspx

    This code uses the second method as it reduces the end users work when it comes to playing with the simulator.

    The new functionality for the left thumb control in the x direction required calculating the LatLong coordinate and altitude of where the plane is suppose to go to. A distance in which to travel is determined based on how much the user pushing the thumb control forward. This distance is then broken into two components, a horizontal and vertical component. These components are determined using the pitch of the camera and some trig. Using the horizontal component and the heading of the camera the destination coordinate of the plane is determined. These calculations are based on the following article: http://rbrundritt.spaces.live.com/blog/cns!E7DBA9A4BFD458C5!400.entry

    The camera is then update by extending the CameraControl class. An example of how to do this can be found here: http://blogs.msdn.com/virtualearth3d/archive/2008/10/22/camera-control.aspx

    Complete source code for this version of the flight simulator can be downloaded here:

     http://cid-e7dba9a4bfd458c5.skydrive.live.com/self.aspx/VE%20Sample%20code/SimpleFlightSimulatorV1.1.zip