Introduction

In Part 1 we created a Silverlight control that enabled us to add pushpins to a Bing Map using JavaScript. In this part we’ll use that ability to create a web part that will connect to a SharePoint list of location data and render that data as pushpins on the map.

The SharePoint Web Part

I developed the Silverlight control on my local machine so will be switching to a VM with SharePoint and the VSeWSS 1.3 extensions installed for this part. Open up Visual Studio and create a new SharePoint Web Part project. I’ve called mine BingMapsWebPart because it’s late at night and I can’t think of anything else. I’ve also renamed the web part class to BingMap.

To render the Silverlight control within the web part we need to emit the same markup that we had in the Test Page created for us in Part 1. for cleanliness I’ve stored most of the HTML tags, attribute strings and their values in a Constants class (you don’t have to of course). The Constants class has been added to the project and looks like the following: –

public class Constants
{
 
    public const string Id = "id";
    public const string Data = "data";
    public const string Type = "type";
    public const string Name = "name";
    public const string None = "none";
    public const string Value = "value";
    public const string Width = "width";
    public const string Height = "height";
    public const string Border = "border";
    public const string Hidden = "hidden";
    public const string Visibility = "visibility";
 
    public const string HtmlDiv = "div";
    public const string HtmlObject = "object";
    public const string HtmlParam = "param";
    public const string HtmlIFrame = "iframe";
 
    public const string DivId = "silverlightControlHost";
 
    public const string ObjectData = "data:application/x-silverlight-2,";
    public const string ObjectType = "application/x-silverlight-2";
 
    public const string IFrameId = "_sl_historyFrame";
 
    public const string ParamSourceName = "source";
    public const string ParamSourceValue = "_layouts/BingMaps/BingMapWebPart.xap";
    public const string ParamOnErrorName = "onError";
    public const string ParamOnErrorValue = "onSilverlightError";
    public const string ParamOnLoadName = "onLoad";
    public const string ParamOnLoadValue = "onSilverlightLoad";
    public const string ParamBackgroundName = "background";
    public const string ParamBackgroundValue = "white";
    public const string ParamWindowlessName = "windowless";
    public const string ParamWindowlessValue = "true";
    public const string ParamMinRuntimeName = "minRuntimeVersion";
    public const string ParamMinRuntimeValue = "3.0.40624.0";
    public const string ParamAutoUpgradeName = "autoUpgrade";
    public const string ParamAutoUpgradeValue = "true";
 
    public const string GetSilverlightLink = "http://go.microsoft.com/fwlink/?LinkID=149156&v=3.0.40624.0";
    public const string GetSilverlightImage = "http://go.microsoft.com/fwlink/?LinkId=108181";
    public const string GetSilverlightAltText = "Get Microsoft Silverlight";
 
    public const string CssTextDecoration = "text-decoration";
    public const string CssBorderStyle = "border-style";
 
    public const string ScriptKeySilverlight = "silverlight_js";
    public const string ScriptKeySilverlightOnLoad = "silverlightOnLoad_js";
 
    public const string SilverlightScriptSource = "_layouts/BingMaps/Silverlight.js";
 
    public const string HtmlKeyMapLocation = "MapLocation";
 
}

The only two you really need to care about in here are ParamSourceValue and SilverlightScriptSource. The former is the relative path to the Silverlight .xap file we created in Part 1 while the latter is the path to the standard Silverlight.js file. In this example I’ve copied both files to the LAYOUTS folder under the 12 hive on the SharePoint box. If you planning to put the files somewhere else be sure to update the values in the Constants class.

Back in our BingMap class set up some private and public properties: –

private Panel _controlHost;
private string _latitudeColumn = "Latitude";
private string _longitudeColumn = "Longitude";
private string _titleColumn = "LinkTitle";
private IWebPartTable _provider;
private ICollection _tableData;
 
[WebDisplayName("Latitude Column"),
 WebBrowsable(true),
 Personalizable(PersonalizationScope.Shared),
 WebDescription("The column from the list that stores the Latitude."),
 Category("Map Settings")]
public string LatitudeColumn
{
    get { return _latitudeColumn; }
    set { _latitudeColumn = value; }
}
 
[WebDisplayName("Longitude Column"),
 WebBrowsable(true),
 Personalizable(PersonalizationScope.Shared),
 WebDescription("The column from the list that stores the Longitude."),
 Category("Map Settings")]
public string LongitudeColumn
{
    get { return _longitudeColumn; }
    set { _longitudeColumn = value; }
}
 
[WebDisplayName("Title Column"),
 WebBrowsable(true),
 Personalizable(PersonalizationScope.Shared),
 WebDescription("The column from the list that stores the title for the information window."),
 Category("Map Settings")]
public string TitleColumn
{
    get { return _titleColumn; }
    set { _titleColumn = value; }
}

Above we have private properties for an ASP panel control (we’ll have this run at the server side to enable us to check if the control has already been created), three private properties and their public accessors for setting the columns in the SharePoint list that we want to use for Latitude, Longitude and Title information and two private properties (_provider and _tableData) to allow us to consume the data from the SharePoint list.

The next stage is to create the markup for the Silverlight control. We’ll do this inside a CreateMapControl method as follows: –

private void CreateMapControl()
{
    if (_controlHost == null)
    {
        if (!this.Page.ClientScript.IsClientScriptIncludeRegistered(Constants.ScriptKeySilverlight))
        {
            this.Page.ClientScript.RegisterClientScriptInclude(this.GetType(), Constants.ScriptKeySilverlight, Constants.SilverlightScriptSource);
        }
        _controlHost = new Panel();
        _controlHost.ID = Constants.DivId;
        HtmlGenericControl obj = new HtmlGenericControl(Constants.HtmlObject);
        obj.Attributes.Add(Constants.Data, Constants.ObjectData);
        obj.Attributes.Add(Constants.Type, Constants.ObjectType);
        obj.Attributes.Add(Constants.Width, Unit.Percentage(100).ToString());
        obj.Attributes.Add(Constants.Height, Unit.Percentage(100).ToString());
        HtmlGenericControl paramSource = new HtmlGenericControl(Constants.HtmlParam);
        paramSource.Attributes.Add(Constants.Name, Constants.ParamSourceName);
        paramSource.Attributes.Add(Constants.Value, Constants.ParamSourceValue);
        HtmlGenericControl paramOnError = new HtmlGenericControl(Constants.HtmlParam);
        paramOnError.Attributes.Add(Constants.Name, Constants.ParamOnErrorName);
        paramOnError.Attributes.Add(Constants.Value, Constants.ParamOnErrorValue);
        HtmlGenericControl paramOnLoad = new HtmlGenericControl(Constants.HtmlParam);
        paramOnLoad.Attributes.Add(Constants.Name, Constants.ParamOnLoadName);
        paramOnLoad.Attributes.Add(Constants.Value, Constants.ParamOnLoadValue);
        HtmlGenericControl paramBackground = new HtmlGenericControl(Constants.HtmlParam);
        paramBackground.Attributes.Add(Constants.Name, Constants.ParamBackgroundName);
        paramBackground.Attributes.Add(Constants.Value, Constants.ParamBackgroundValue);
        HtmlGenericControl paramWindowless = new HtmlGenericControl(Constants.HtmlParam);
        paramWindowless.Attributes.Add(Constants.Name, Constants.ParamWindowlessName);
        paramWindowless.Attributes.Add(Constants.Value, Constants.ParamWindowlessValue);
        HtmlGenericControl paramMinRuntime = new HtmlGenericControl(Constants.HtmlParam);
        paramMinRuntime.Attributes.Add(Constants.Name, Constants.ParamMinRuntimeName);
        paramMinRuntime.Attributes.Add(Constants.Value, Constants.ParamMinRuntimeValue);
        HtmlGenericControl paramAutoUpgrade = new HtmlGenericControl(Constants.HtmlParam);
        paramAutoUpgrade.Attributes.Add(Constants.Name, Constants.ParamAutoUpgradeName);
        paramAutoUpgrade.Attributes.Add(Constants.Value, Constants.ParamAutoUpgradeValue);
        HtmlAnchor a = new HtmlAnchor();
        a.HRef = Constants.GetSilverlightLink;
        a.Style.Add(Constants.CssTextDecoration, Constants.None);
        HtmlImage img = new HtmlImage();
        img.Src = Constants.GetSilverlightImage;
        img.Alt = Constants.GetSilverlightAltText;
        img.Style.Add(Constants.CssBorderStyle, Constants.None);
        HtmlGenericControl iframe = new HtmlGenericControl(Constants.HtmlIFrame);
        iframe.Attributes.Add(Constants.Id, Constants.IFrameId);
        iframe.Style.Add(Constants.Visibility, Constants.Hidden);
        iframe.Style.Add(Constants.Height, Unit.Pixel(0).ToString());
        iframe.Style.Add(Constants.Width, Unit.Pixel(0).ToString());
        iframe.Style.Add(Constants.Border, Unit.Pixel(0).ToString());
        a.Controls.Add(img);
        obj.Controls.Add(paramSource);
        obj.Controls.Add(paramOnLoad);
        obj.Controls.Add(paramBackground);
        obj.Controls.Add(paramWindowless);
        obj.Controls.Add(paramMinRuntime);
        obj.Controls.Add(paramAutoUpgrade);
        obj.Controls.Add(a);
        _controlHost.Controls.Add(obj);
        _controlHost.Controls.Add(iframe);
        this.Controls.Add(_controlHost);
    }
}

Now that’s a fair amount of code, but essentially all it does is build a control tree that has the same markup that we had in our Test Page. Now we need to craft our JavaScript function that we want to call when the Silverlight control has loaded: –

private void RegisterSilverlightOnLoadFunction()
{
    try
    {
        CreateMapControl();
        if (_tableData != null)
        {
            if (!this.Page.ClientScript.IsClientScriptBlockRegistered(Constants.ScriptKeySilverlightOnLoad))
            {
                StringBuilder sb = new StringBuilder();
                sb.Append("function ");
                sb.Append(Constants.ParamOnLoadValue);
                sb.Append("(sender, args) {");
                sb.Append("\r\n\t");
                sb.Append("var bingMapsControl = sender.getHost();");
                sb.Append("\r\n\t");
                foreach (DataRowView rowView in _tableData)
                {
                    string title = rowView.Row[this._titleColumn].ToString();
                    double latitude = double.Parse(rowView.Row[_latitudeColumn].ToString());
                    double longitude = double.Parse(rowView.Row[_longitudeColumn].ToString());
                    sb.Append("var l = bingMapsControl.content.services.createObject('");
                    sb.Append(Constants.HtmlKeyMapLocation);
                    sb.Append("');");
                    sb.Append("\r\n\t");
                    sb.Append("l.Title = '");
                    sb.Append(title);
                    sb.Append("';");
                    sb.Append("\r\n\t");
                    sb.Append("l.Latitude = ");
                    sb.Append(latitude);
                    sb.Append(";");
                    sb.Append("\r\n\t");
                    sb.Append("l.Longitude = ");
                    sb.Append(longitude);
                    sb.Append(";");
                    sb.Append("\r\n\t");
                    sb.Append("bingMapsControl.content.Communicator.AddLocation(l);");
                    sb.Append("\r\n\t");
                }
                sb.Append("\r\n");
                sb.Append("}");
                sb.Append("\r\n");
                this.Page.ClientScript.RegisterClientScriptBlock(this.GetType(), Constants.ScriptKeySilverlightOnLoad, sb.ToString(), true);
            }
        }
    }
    catch
    { }
}

This will emit the same JavaScript that we had in our test page. The major difference here is that we’re looping through each row in our SharePoint list and calling the AddLocation() method. In the overridden CreateChildControls() method add a call to our CreateMapControl function: –

protected override void CreateChildControls()
{
    base.CreateChildControls();
    CreateMapControl();
}

The only thing left to code now is the web part connection so add the following two methods to the web part: –

[ConnectionConsumer("Location Map Data")]
public void GetConnectionInterface(IWebPartTable provider)
{
    TableCallback callback = new TableCallback(ReceiveTable);
    _provider = provider;
    provider.GetTableData(callback);
}
 
public void ReceiveTable(object providerTable)
{
    _tableData = providerTable as ICollection;
    RegisterSilverlightOnLoadFunction();
}

Because the data connection works asynchronously our GetConnectionInterface method defines a callback method that will be executed once the data has been returned from the list. Once we have the data the callback function, ReceiveTable, can write the required JavaScript.

Now that’s all we need to do to get the web part to function. This version (for brevity’s sake) has zero error handling so before you put this thing anywhere near a non-development SharePoint machine make sure that you include some exception handling.

If you have the URL of a SharePoint site in the “Start browser with URL” field in Project Properties > Debug then you can hopefully just right-click the project in Visual Studio and select Deploy. Again, if you’re going to a production environment you’ll want to package the web part code (plus the .xap and .js files from the Silverlight project) as a .wsp file.

What we need now is a SharePoint site (hopefully you’ve already got one of these lying around). If you’ve already got a list with location data then great. If not, then just create a new custom list with columns for Latitude and Longitude (both numbers).

Once you’ve got your Locations list add your web part and a list view web part to a page (new or otherwise). You’ll need to set the height of the Bing Map web part in pixels to get it to show. What you should have now is a blank map of the world plus your empty Locations list. Go ahead and add some data into your list.

Bing Map Web Part Connections

Once you’ve done that, change the settings on the Bing Map web part by clicking the menu arrow at the top right hand corner of the control and selecting Modify Shared Web Part. Once the page reloads in edit mode, check that the column names we’ve defined in the map web part match those in the locations list. In the right-hand panel in edit mode expand the Map Settings section and ensure the values in the Latitude, Longitude and Title Columns fields are valid column names in your list. Then, click the edit menu arrow at the top right of the control and select Connections > Get Location Map Data From > Locations (or whatever your list happens to be called). The page will reload again and this time you should see pushpins on the map at the latitude and longitudes specified in your list. Click Exit Edit Mode in the top right to view the page.

The final result should resemble something like the following (I’ve zoomed in so that we can distinguish between the Glasgow and Edinburgh pushpins.

Bing Map Web Part

Introduction

OK, another post in response to a forum question. This time on SharePoint Overflow. I was looking for a way to display pushpins on a Bing Map Silverlight control using data from a SharePoint list. I’ve split this post into two parts mainly because there’s a fair amount of code involved to just to render the Silverlight control in the web part, never mind the building the required JavaScript in managed code, so we’ll leave that for another day (or night).

The answer to the question in this case is to use the HTML Bridge that enables interaction between HTML and Managed Code, specifically it allows us expose methods in managed code to JavaScript, but we’ll get that part later. First up let’s create the Silverlight control.

The Silverlight Control

This part is fairly straightforward so I won’t go into the details here. This article will give you all you need to get started. One thing to note is that to test the Silverlight control I’ve made use of the web project that Visual Studio can create for you when you create a new Silverlight project so if you want to follow along with that particular section be sure to let Visual Studio create the web project for you. You can, however, skip the “Change the Map Mode using C#” step if you want. Before we move on to the code we need to add a layer to the map so that we have somewhere to put the pushpins so add the following fragment to the <m:Map> element in the MainPage.xaml file: –

<m:Map.Children>
	<m:MapLayer x:Name="Pushpins">
	</m:MapLayer>
</m:Map.Children>

Now we have somewhere to put them we’ll write the code that places the pushpins on the map, so open up the MainPage.xaml.cs file. To be able to use the HTML Bridge and Bing Map Control we need to add some references so add the following lines to the top of the file: –

using System.Windows.Browser;
using Microsoft.Maps;
using Microsoft.Maps.MapControl;
using Microsoft.Maps.MapControl.Core;

Now that we have the reference we can add a custom location class that will contain the latitude and longitude coordinates enabling us to position the pin on the map. Add the following class to the MainPage.xaml.cs file within your namespace: –

[ScriptableType]
public class MapLocation
{
    [ScriptableMember]
    public string Title { get; set; }
    [ScriptableMember]
    public double Latitude { get; set; }
    [ScriptableMember]
    public double Longitude { get; set; }
}

The ScriptableType attribute allows us to make use of the public properties, methods and events of a managed code object in JavaScript. ScriptableMember indicates that the specific property is available to JavaScript callers. Technically this isn’t required in this scenario as we’ve decorated the entire class with the ScriptableType attribute but it won’t break it either.

Next we can add our method to add a pushpin to the map that will be called by JavaScript. Add the following lines to the MainPage class in your MainPage.xaml.cs file: –

[ScriptableMember]
public void AddLocation(MapLocation location)
{
    Pushpin pin = new Pushpin();
    pin.Location = new Location(location.Latitude, location.Longitude);
    pin.Name = location.Title;
    pin.Tag = location.Title;
    Pushpins.Children.Add(pin);
}

The ScriptableMember attribute indicates that this method is available to us from JavaScript. To complete the MainPage class changes add the following lines to the constructor after the InitializeComponent(); call: –

HtmlPage.RegisterCreateableType("MapLocation", typeof(MapLocation));
HtmlPage.RegisterScriptableObject("Communicator", this);

This first line registers our MapLocation class for use in JavaScript with the key of “MapLocation” (why complicate things?). The second line registers the MainPage class for the same purpose. This is all we need to do to the Silverlight control but before we dive into creating the SharePoint web part lets test that we can indeed call the method from JavaScript by using the test page in the web project that Visual Studio has created for us.

The JavaScript

I’ve called my solution BingMapWebPart so I’m opening the BingMapWebPartTestPage.aspx file (yours might be named more succinctly). There should already be an onSilverlightError function in the head of the page. Below that we can add our JavaScript function: –

function onSilverlightLoad(sender, args) {
    var bingMapsControl = sender.getHost();
    var l = bingMapsControl.content.services.createObject("MapLocation");
    l.Title = "API Software - Glasgow";
    l.Latitude = 55.864438;
    l.Longitude = -4.262776;
    bingMapsControl.content.Communicator.AddLocation(l);
}

This function should add a pushpin to the map at the location of API Software’s offices in Bath Street, Glasgow. Note the use of the keys we defined in the Silverlight control. To create a new MapLocation object on the client we’ve called the content.services.createObject method of the control, passing in the “MapLocation” key, then to call the AddLocation() method of the MainPage class we’ve called the AddLocation() method of the content.Communicator object. The last thing to do is ensure that this function is called once the Silverlight control has loaded, so add the following line to the existing list of params in the object tag for the control: –

<param name="onLoad" value="onSilverlightLoad" />

So if this all works, when you press F5 you’ll see a pushpin pointing to Glasgow on a map of the world. In Part 2 we’ll turn this into a SharePoint web part.

References

Introduction

If, like me, you have a bizarre fascination with maps you can’t have failed to notice that Google Maps are appearing on an ever-increasing number of websites, some for valid and useful reasons, some not so much. Now, if you head over to https://maps.google.com/ and zoom in on a major US city you might notice that there is a Traffic button at the top right of the map. Click this and you’ll get traffic data overlaid on the map, looking a bit like Figure 1 below.


Figure 1: New York Traffic

Now if you zoom in somewhere in the UK you will get this traffic info, unless you’re here in Scotland, but the overlays seem a bit off to me and I’ve yet to see any icons that will display message windows with more information. I’ve no doubt that Google will address these issues in due course, but in the meantime there is a way of getting detailed traffic info (albeit without the nice coloured overlays) onto a Google Map, namely TPEG.

What’s TPEG?

TPEG stands for Transport Protocol Experts Group and they have agreed standards for Binary and XML transmission of Traffic and Travel Information (TTI). For more information have a look at the TPEG links in the References section at the end of this post. In the UK, Auntie Beeb is helpfully publishing TPEG data in both Binary and XML formats (and you thought they were all about scamming phone voters). The XML document for road traffic information lives at http://www.bbc.co.uk/travelnews/tpeg/en/local/rtm/rtm_tpeg.xml and there’s a corresponding public transport document at http://www.bbc.co.uk/travelnews/tpeg/en/pti/pti_tpeg.xml. In the next few steps I’ll demonstrate how to display a subset of this data on a very simple ASP.NET website using Google Maps.

Creating the Website

I’m going to be using Visual Studio 2008 for this but 2005 will do just the same. Open up Visual Studio and create a new Web Site project. In addition to the App_Data folder that’s created for you, add an App_Code and a Bin folder to the project. Firstly, download the zip file from Subgurim.NET which wraps the functionality of a Google Map into an ASP.NET web control and extract the GMaps.dll into the Websites Bin folder.

Next, get yourself a copy of the road traffic information XML file (just download it from the link above) and copy it into the App_Data folder.

Now, to make working with the data a little bit easier, we’ll create a schema definition for the XML (we can’t download one because it’s based on a DTD currently) and then use xsd.exe to get ourselves a class file. Open up the rtm_tpeg file in Visual Studio, go to the XML menu and select Create Schema. When you get the resulting rtm_tpeg.xsd file on the screen save that into the App_Data folder too.

Open the Visual Studio Command Prompt, navigate to your App_Data folder, and execute the following command to create a C# class in your App_Code folder: xsd.exe rtm_tpeg.xsd /c /language:cs /o:../App_Code. If you’re working in VB.NET simply change the language switch to /language:vb. The structure of your Website should new resemble Figure 2.


Figure 2: Website Structure

Now, we can add the map control to our web page and start adding information windows to it. Open up your default.aspx page and edit it so that it resembles the following: –

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="default.aspx.cs" Inherits="_default" %>
<%@ Register Assembly="GMaps" Namespace="Subgurim.Controles" TagPrefix="gmap" %> 
<!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" xmlns:v="urn:schemas-microsoft-com:vml">
    <head runat="server">
        <title>TPEG and Google Maps</title>
        <style type="text/css">
        html, body
        {
            margin: 0px 0px 0px 0px;
            padding: 0px 0px 0px 0px;
            height: 100%;
        }
        v:*
        {
            behavior: url(#default#VML);
        }
        </style>
    </head>
    <body>
        <form id="mapForm" runat="server">
            <gmap:GMap ID="map" runat="server" Width="100%" Height="100%" />
        </form>
    </body>
</html>

Open the default.aspx.cs and modify it to look like: –

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;
using Subgurim.Controles;
 
public partial class _default : System.Web.UI.Page
{
 
    protected void Page_Load(object sender, EventArgs e)
    {
        if (!Page.IsPostBack)
        {
            map.setCenter(new GLatLng(55.864536, -4.263103));
            map.GZoom = 12;
            map.addControl(new GControl(GControl.preBuilt.SmallMapControl));
            string uri = “http://www.bbc.co.uk/travelnews/tpeg/en/local/rtm/rtm_tpeg.xml”;
            BindData(uri);
        }
    }
 
    private void BindData(string uri)
    {
        XmlDocument xml = new XmlDocument();
        xml.Load(uri);
        XmlNodeReader nr = new XmlNodeReader(xml.DocumentElement);
        XmlSerializer xs = new XmlSerializer(typeof(tpeg_document));
        tpeg_document tpeg = (tpeg_document)xs.Deserialize(nr);
        for (int i = 0; i < tpeg.Items.Length; i++)
        {
            if (tpeg.Items[i] is tpeg_message)
            {
                tpeg_message message = tpeg.Items[i] as tpeg_message;
                if (message.Item is road_traffic_message)
                {
                    road_traffic_message rtmessage = message.Item as road_traffic_message;
                    for (int j = 0; j < rtmessage.Items.Length; j++)
                    {
                        if (rtmessage.Items[j] is location_container)
                        {
                            location_container loc = rtmessage.Items[j] as location_container;
                            for (int k = 0; k < loc.Items.Length; k++)
                            {
                                if (loc.Items[k] is location_coordinates)
                                {
                                    location_coordinates coords = loc.Items[k] as location_coordinates;
                                    for (int l = 0; l < coords.Items.Length; l++)
                                    {
                                        if (coords.Items[l] is WGS84)
                                        {
                                            WGS84 latLong = coords.Items[l] as WGS84;
                                            double latitude = double.Parse(latLong.latitude);
                                            double longitude = double.Parse(latLong.longitude);
                                            StringBuilder sb = new StringBuilder();
                                            sb.Append(<p>);
                                            sb.Append(message.summary[0].Value);
                                            sb.Append(</p>);
                                            GMarker marker = new GMarker(new GLatLng(latitude, longitude));
                                            map.addInfoWindow(new GInfoWindow(marker, sb.ToString(), false));
                                            map.addGMarker(marker);
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}

Briefly, we set centre the map on Glasgow, set the zoom level and add the small map control. Set the uri parameter to the XML TPEG feed (if you find that this takes a while to download you can always use the saved version in your App_Data folder). The BindData() method de-serialises the XML into our tpeg_document class and then starts to loop through the road traffic messages in the document. Without going into the gory details of the TPEG schema, the message contains a summary, which we write to the infoWindow, and location co-ordinates which allow us to add the marker icon in the right place.

Hit the F5 key to run the application, allowing Visual Studio to modify your web.config file if you like, and marvel (if you’re easily impressed) at live traffic information on a Google Map.


Figure 3: Glasgow Traffic

What Next

You’ll notice on the figure above that you often get two markers closely spaced on the same stretch of road. These tend to be the start and end points of heavy congestion and Google would display these as coloured overlays on the map. We can render a polyline on top of the map given a start and end point but the problem with that is it will be a straight line and won’t follow the road (probably). If you can find out how to overlay a line following the road then you’d be getting pretty close to what Google can do.

References

Maps

TPEG