Implementation of Touring Feature for Virtual Earth

by Gong Liu February 21, 2009 20:52

If you have ever used Google Earth, you will probably be familiar with the touring feature, which plays back a series of points, i.e. putting a point on the map center, opening its balloon which may contain photos and/or text, staying for a fixed time period, and then moving on to the next point. It is almost like a slide show, but at the same time shows the location of current point on the map. Virtual Earth does not have the touring feature built-in. However, we can implement the feature ourselves using Virtual Earth SDK. Please first visit this link to see a demo of the touring feature in Virtual Earth, and then read the rest of the post to see how it is implemented.

In the demo, there are 5 pushpins and their data is defined by the following 2D array:

    var data = new Array(
        new Array(34.042182, -118.232889, "homer0.jpg", 289, 300, "Homer screaming"), 
        new Array(34.045917, -118.239462, "homer1.gif", 290, 267, "D'OH!"),   
        new Array(34.049360, -118.245736, "homer2.jpg", 210, 210, "Homer having a donut"), 
        new Array(34.051448, -118.252511, "homer3.jpg", 300, 400, "Homer having a beer"), 
        new Array(34.057442, -118.259504, "homer4.jpg", 219, 167, "Homer choking Bart") );

where each pushpin has these fields: latitude, longitude, image name, image width, image height and caption. Of course, in a real application the data will probably come from a back end database. The data is loaded to the map with this script:

    function LoadData()
    {  
        var layer = new VEShapeLayer();
        map.AddShapeLayer(layer);
        //create array of VEShape and bulk add to the layer
        var shapes = new Array();
        for (var i = 0; i < data.length; i++)
        {
            var shape = new VEShape(VEShapeType.Pushpin, new VELatLong(data[i][0], data[i][1]));
            shape.SetTitle(data[i][2]);
            var desc = "<div style=\"width:200px\"><img src=\"photos/" + data[i][2] + "\" width=\"200\" height=\"" + Math.round(200 * data[i][4]/data[i][3],0) + "\" border=\"0\"></div><div style=\"width:200px\">" + data[i][5] + "</div>";
            shape.SetDescription(desc);
            shapes[i] = shape;
        }
        layer.AddShape(shapes);  
        AdjustView(layer);
    }

This script first creates a layer, then creates an array of shapes (pushpins) based on the data, and then adds the pushpins to the layer in bulk. Notice that an image width is fixed to 200 pixels and the infobox width is fixed to 233 pixels accordingly. This is to prevent a too big infobox from showing. In other words, images may be dynamically resized to fit the infobox.

Once the data is loaded, the following script is used to adjust the map view so that all the pushpins in the layer are visible on the map:

    function AdjustView(layer)
    {
        var rect = layer.GetBoundingRectangle();
        var count = layer.GetShapeCount();
        map.SetMapView(rect);
        if (count <= 1)
        {
            map.SetZoomLevel(7);
        }
    } 

The following script and variables are directly related to playing a tour:

    var currShapeIdx = -1;
    var theShape = null;
    var pause = true;
    var customBehavior = false;

    function Start()
    {
        currShapeIdx = 0;
        pause = false;
        Play();
    }

    function Stop()
    {
        currShapeIdx = -1;
        pause = true;
    }

    function Pause()
    {
        pause = true;
    }
            
    function Restart()
    {
        pause = false;
        Play();
    }
            
    function Play()
    {
        var layer = map.GetShapeLayerByIndex(1);  
        var n = layer.GetShapeCount();
        var img = document.getElementById("currImg");
        if (currShapeIdx >= 0 && currShapeIdx < n && !pause)
        {
            var sideWidth = (typeof( window.innerWidth ) == 'number' )? Math.round(window.innerWidth * 0.2) : Math.round(document.documentElement.clientWidth * 0.2);  
            sideWidth -= 10;     
            img.src = "photos/" + data[currShapeIdx][2];
            img.width = (data[currShapeIdx][3] > sideWidth)? sideWidth : data[currShapeIdx][3];
            img.style.visibility = "visible";  
            theShape = layer.GetShapeByIndex(currShapeIdx);
            var pts = theShape.GetPoints();
            customBehavior = true;
            map.PanToLatLong(pts[0]);
            //map.SetCenter(pts[0]);
            currShapeIdx++;
            var chkLoop = document.getElementById("loop");
            if (currShapeIdx == n && chkLoop.checked)
                currShapeIdx = 0;
            var selInterval = document.getElementById("interval");
            var i = selInterval.options[selInterval.selectedIndex].value;
            window.setTimeout("Play()", 1000 * i);  
        }
        else
        {
            img.style.visibility = "hidden";
            theShape = null;
        }
    }
            
    function OnMapMoved()
    {
        if (customBehavior)
            window.setTimeout("map.ShowInfoBox(theShape)", 10);
        customBehavior = false;
    }

Here are some key points about this script. First, the Play() function is called recursively via the javascript function window.setTimeout(). The calling interval is specified by the user. Second, each time the Play() function is called, the current pushpin is retrieved and the map is moved via the VE method PanToLatLong() so that the current pushpin is at the map center. The current pushpin is tracked by the variables currShapeIdx and theShape. Third, when the map is moved in position, the OnEndPan event is fired and the event handler OnMapMoved() is executed, which opens the infobox of the current pushpin. It is important to call the ShowInfoBox() method via the javascript function window.setTimeout(), and only call it once right after the Play() function is called. Fourth, the pause/restart status is tracked by the boolean variable pause. Once the touring is started, pressing the pause/restart button toggles the call between Pause() and Restart() functions. Finally, the width of the image on the sidebar is determined by the width of the sidebar or the original image width, whichever is smaller. The width of the sidebar is 20% of the width of the browser window.

Again, to see the touring feature in action and the complete javascript code, click here.

Note if you set the interval too small while the points are too far apart, you may find that some points are skipped during touring because it takes too long time to pan from one point to the next. In this case you will have to either increase the playback interval or zoom out the map a few notches.

About

A seasoned computer professional. A tofu culture evangelist...
more >>

Tag Cloud

Calendar

<<  April 2017  >>
MoTuWeThFrSaSu
272829303112
3456789
10111213141516
17181920212223
24252627282930
1234567

View posts in large calendar
Copyright © 2008-2011 Gong Liu. All rights reserved. | credits | contact me
The content on this site represents my own personal opinions, and does not reflect those of my employer in any way.