Bing Maps Tile System: A bit of theory and an app to test it in Windows Phone 7

A few weeks back my good friend, colleague and Microsoft trainer par excellence, Elias Markelis asked me to do a joint webinar on Windows Phone and Bing Maps, part of Microsoft’s Live Virtual Academy series. The idea being that since I am the ‘GIS guy’ I will talk a bit more about the spatial / map aspects and not just the code.

(BTW, you can also find the full webinar at http://www.techdays.gr/videos/4056.html but take note: The slides are in English but seminar was delivered in Greek.)

Elias has now written a 3-part blog post to cover most of the things we talked about in the webinar: You can find the links here:

These are very detailed step-by-step guides on how to develop your first Windows phone map-based app, utilizing the Geocoding and Routing Services, GPS, the emulator and lots of other goodies to get you started.

But this is the code only. In this post, which can be thought as an ‘addendum’ to Elias’ series, I would talk about how Bing Maps work, their tile system, scales and projection systems. The post is mainly geared towards Silverlight and Windows Phone developers who never worked with GIS and mapping systems before. GIS gurus, feel free to skip directly to the code!

Should I care? Just show me some code!

Well, you should care really. Understanding the tile system and projections would make a huge difference in real-world applications where you would invariably need to integrate with other spatial data in different formats. For example you may have to display geometry data from a SQL Server Database, or add a WMS layer. In any of these cases, you would need to understand terms like ‘projection system’, ‘geographic coordinates’ and ‘map scales’. And also understand how these apply in Bing Maps.

But fear not, its not going to be ALL theory. I would also show you how to develop a Bing Maps app which will help you visualize some of the theory and maths behind Bing Maps.

The Basics – Bing Maps tile system

Now, before I start, I should point out that there is an excellent article in MSDN which explains the Bing Maps Tile System in great detail, including all the maths behind it. It even includes all the C# code that implements it. In actual fact, I would be using bits of this code in my own example. But here, I will just give you the basics to get you started in the form of a list of simple, clear statements. So here it goes:

  • Bing Maps uses a single map projection system called Mercator. The projection is necessary since Bing Maps displays the whole earth, the earth, well, is round, so in order to see it a screen (which, you know, is flat) we have to ‘project’ it. The image below depicts this concept. (This is not the Mercator projection BTW, just a peeled orange ).
image
The Earth is round, a computer screen ain
  • To accurately locate a point or any other feature on the map, the WGS84 Spatial Reference System (SRID) is used which uses geographic coordinates (Latitude and Longitude denoted also as φ,λ). This is the same SRID used by the GPS devices (see the link here?)
  • Map scale is another feature of any map. It denotes the ratio between a real-world distance and the map distance at a specified unit. For example a map at a scale of 1:100,000 with map units in meters, means that a meter measured on the map (or you screen) is a 100,000m in the real world. One of the things to point out here is that scale only makes sense and applies on maps on a screen or on paper. Data itself does not have scale.For example, the question of what is the scale of a point at coordinates 40,2345, 22.87361 does not make sense!
  • In very simple terms Bing Maps maps the world using satellite images stitched together. As you can appreciate, these are terrabytes of data we are talking about.To optimize performance these images are divided into tiles of 256×256 pixels each.
  • Bing Maps has the concept of zoom levels. There are 23 zoom levels where 1 displays the whole earth and 23 zooms at street level. When you zoom in or out at a different zoom level, different tiles (and hence satellite images) are displayed.
  • Tiles have their own coordinate system (a “Tile XY coordinate”) which can also be denoted as a single string called a quadkey. As explained in the Bing Maps Tile System article:

To optimize the indexing and storage of tiles, the two-dimensional tile XY coordinates are combined into one-dimensional strings called quadtree keys, or “quadkeys” for short. Each quadkey uniquely identifies a single tile at a particular level of detail…

The example below displays the quadkeys for Levels 1-3 (image from the same article):

image
Quadkeys for zoom levels 1 to 3

Some code (at last)

Just to ‘visualize’ what we talked about, we will create a Windows Phone application with Bing Maps which will accomplish a few simple tasks:

  • Double-tapping the map will display the quadkey value of the current tile in a message box.
  • Panning the map, will display the latitude, longitude coordinates in a textblock.
  • The map will also draw the tiles used in the current map extent and display their quadkey value

To start off with, start a new Windows Phone 7 project in Visual Studio 2010 and add the maps control I won’t go into any details on how to do this, there are a lot of articles abound. You can try Elias’ Windows Phone location awareness– the basics or one of mine Sterling Database for Windows Phone 7, Silverlight and Bing Maps

We will now add 3 event handlers to the map control. One for double-tapping on the map, one for mouse move and one for the ViewChangeEnd which fires when the map has finished zooming or panning. Our XAML file should now look like this:

Code Snippet
  1. <phone:PhoneApplicationPage
  2.     x:Class=”BingMapsBasics.MainPage”
  3.     xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation&#8221;
  4.     xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml&#8221;
  5.     xmlns:phone=”clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone”
  6.     xmlns:shell=”clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone”
  7.     xmlns:d=”http://schemas.microsoft.com/expression/blend/2008&#8243;
  8.     xmlns:mc=”http://schemas.openxmlformats.org/markup-compatibility/2006&#8243;
  9.     xmlns:my=”clr-namespace:Microsoft.Phone.Controls.Maps;assembly=Microsoft.Phone.Controls.Maps”
  10.     mc:Ignorable=”d” d:DesignWidth=”480″ d:DesignHeight=”768″
  11.     FontFamily=”{StaticResource PhoneFontFamilyNormal}
  12.     FontSize=”{StaticResource PhoneFontSizeNormal}
  13.     Foreground=”{StaticResource PhoneForegroundBrush}
  14.     SupportedOrientations=”Portrait” Orientation=”Portrait”
  15.     shell:SystemTray.IsVisible=”True”>
  16.     <!–LayoutRoot is the root grid where all page content is placed–>
  17.     <Grid x:Name=”LayoutRoot” Background=”Transparent”>
  18.         <Grid.RowDefinitions>
  19.             <RowDefinition Height=”Auto”/>
  20.             <RowDefinition Height=”*”/>
  21.         </Grid.RowDefinitions>
  22.         <!–TitlePanel contains the name of the application and page title–>
  23.         <StackPanel x:Name=”TitlePanel” Grid.Row=”0″ Margin=”12,17,0,28″>
  24.             <TextBlock x:Name=”ApplicationTitle” Text=”Bing Maps Demo” Style=”{StaticResource PhoneTextNormalStyle}“/>
  25.             <TextBlock x:Name=”PageTitle” Text=”Map Basics” Margin=”9,-7,0,0″ Style=”{StaticResource PhoneTextTitle1Style}“/>
  26.         </StackPanel>
  27.         <Grid x:Name=”ContentPanel” Grid.Row=”1″ Margin=”12,0,12,0″>
  28.             <Grid.RowDefinitions>
  29.                 <RowDefinition Height=”50″/>
  30.                 <RowDefinition Height=”*”/>
  31.             </Grid.RowDefinitions>
  32.             <my:Map Grid.Row=”1″ x:Name=”demoMap” Margin=”0,0,0,0″
  33.                     CredentialsProvider=”YOURBINGMAPSCODEHERE”
  34.                     DoubleTap=”demoMap_DoubleTap”
  35.                     MouseMove=”demoMap_MouseMove” ZoomBarVisibility=”Visible”
  36.                    ViewChangeEnd=”demoMap_ViewChangeEnd”>
  37.                 <my:MapLayer x:Name=”quadkeys”></my:MapLayer>
  38.             </my:Map>
  39.             <TextBlock Grid.Row=”0″ x:Name=”txbCoords” VerticalAlignment=”Top” HorizontalAlignment=”Left” Foreground=”White” FontWeight=”Bold” Text=”Coordinates:” />
  40.         </Grid>
  41.     </Grid>
  42. </phone:PhoneApplicationPage>

The MouseMove event handler is listed below:

Code Snippet
  1. private void demoMap_MouseMove(object sender, MouseEventArgs e)
  2.         {
  3.             // Point in pixels
  4.             Point pnt = e.GetPosition(this.demoMap);
  5.             // Geographic point
  6.             GeoCoordinate geoPnt = new GeoCoordinate();
  7.             // Get current point in Lat/Lon
  8.             geoPnt = demoMap.ViewportPointToLocation(pnt);
  9.             //Update the textblock with the current Lat/Lon
  10.             txbCoords.Text = “Lat: “ + geoPnt.Latitude.ToString() +” Lon: “ + geoPnt.Longitude.ToString();
  11.         }

Our DoubleTap handler follows. It basically picks up the Latitude, Longitude location the user tapped on the map, converts them to pixel coordinates, then to Tile coordinates and then to the quadkey value. All the conversion functions can be found in the (yes, you guessed it) MSDN article mentioned above.

Code Snippet
  1. private void demoMap_DoubleTap(object sender, GestureEventArgs e)
  2.         {
  3.             int pX; int pY;
  4.             int tX; int tY;
  5.             Point p = e.GetPosition(this.demoMap);
  6.             GeoCoordinate geo = new GeoCoordinate();
  7.             // Get current point in Lat/Lon
  8.             geo = demoMap.ViewportPointToLocation(p);
  9.             // Convert to pixel coordinates
  10.             LatLongToPixelXY(geo.Latitude, geo.Longitude, Convert.ToInt32(demoMap.ZoomLevel), out pX, out pY);
  11.             //Convert to tile coordinates
  12.             PixelXYToTileXY(pX, pY, out tX, out tY);
  13.             //Get the Quadkey
  14.             string quadKey = TileXYToQuadKey(tX, tY, Convert.ToInt32(demoMap.ZoomLevel));
  15.             MessageBox.Show(“QuadKey: “ + quadKey + “Lat: “ + geo.Latitude.ToString() +” Lon: “ + geo.Longitude.ToString());
  16.         }

Okimage, for the time being leave our last event handler (ViewChangeEnd) empty and compile and run the project. Zoom and pan the map to an area you are interested in. Notice that while panning the geographic coordinates are getting updated in the text block.

Now double click/tap at different areas of the screen. A message box should appear displaying the quadkey of the current tile

That’s ok. Useful-ish. But you have to \guess’ where the different tiles are. What we will do next is display the actual tiles on the map as rectangles and also display the corresponding quadkeys. And we will wire this functionality in the ViewChangEnd event, so it runs only when the map has finished rendering.

The logic behind the drawing of the tiles is pretty simple: Find the extent of the current map (min & max coordinates) and then ‘loop’ or ‘sweep’ the map, 256pixels at a time to get to the next tile, calculating the rectangle (tile) at each iteration and draw this rectangle on the map.

I implemented this ‘sweeping’ using three loops, one by latitude, one by longitude and one ‘diagonal’ where both latitude and longitude were iterated by 256 pixels. Now, I am pretty sure there is a by far smarter way of achieving the same result but its getting late and this was the easiest way I could come up with.

The relevant code is listed below:

Code Snippet
  1.  private void demoMap_ViewChangeEnd(object sender, MapEventArgs e)
  2.  {
  3.      CalcQuadkeys();
  4.  }
  5.  ///<summary>
  6.  /// Calculates the quadkeys
  7.  ///</summary>
  8.  public void CalcQuadkeys()
  9.  {
  10.      MapLayer l = (MapLayer)demoMap.FindName(“quadkeys”);
  11.      l.Children.Clear();
  12.      LocationRect extents = demoMap.BoundingRectangle;
  13.      double minLat = extents.Southwest.Latitude;
  14.      double minLon = extents.Southwest.Longitude;
  15.      double maxLat = extents.Northeast.Latitude;
  16.      double maxLon = extents.Northeast.Longitude;
  17.      double curLat = minLat;
  18.      double curLon = minLon;
  19.      // ‘Scan’ the map in the Latitude direction increasing the Longitude value in each step
  20.      while (curLat <= maxLat)
  21.      {
  22.          //Starting from the ll
  23.          int nextLatInPixels; int nextLonInPixels;
  24.          int curLatInPixels; int curLonInPixels;
  25.          int curTileX; int curTileY;
  26.          double nextLat; double nextLon;
  27.          LatLongToPixelXY(curLat, curLon, Convert.ToInt32(demoMap.ZoomLevel), out curLatInPixels, out curLonInPixels);
  28.          nextLatInPixels = curLatInPixels + 256;
  29.          nextLonInPixels = curLonInPixels – 256;
  30.          // Now convert them back to WGS84 coords
  31.          PixelXYToLatLong(nextLatInPixels, nextLonInPixels, Convert.ToInt32(demoMap.ZoomLevel), out nextLat, out nextLon);
  32.          PixelXYToTileXY(curLatInPixels, curLonInPixels, out curTileX, out curTileY);
  33.          string quadKey = TileXYToQuadKey(curTileX, curTileY, Convert.ToInt32(demoMap.ZoomLevel));
  34.          DrawQuadkeys(quadKey, curLat, curLon, nextLat, nextLon);
  35.          curLat = nextLat;
  36.      }
  37.      //Reset values
  38.      curLat = minLat;
  39.      curLon = minLon;
  40.      // ‘Scan’ the map in the Longitude direction increasing the Longitude value in each step
  41.      while (curLon <= maxLon)
  42.      {
  43.          int nextLatInPixels; int nextLonInPixels;
  44.          int curLatInPixels; int curLonInPixels;
  45.          int curTileX; int curTileY;
  46.          double nextLat; double nextLon;
  47.          LatLongToPixelXY(curLat, curLon, Convert.ToInt32(demoMap.ZoomLevel), out curLatInPixels, out curLonInPixels);
  48.          nextLatInPixels = curLatInPixels + 256;
  49.          nextLonInPixels = curLonInPixels – 256;
  50.          // Now convert them back to WGS84 coords
  51.          PixelXYToLatLong(nextLatInPixels, nextLonInPixels, Convert.ToInt32(demoMap.ZoomLevel), out nextLat, out nextLon);
  52.          PixelXYToTileXY(curLatInPixels, curLonInPixels, out curTileX, out curTileY);
  53.          string quadKey = TileXYToQuadKey(curTileX, curTileY, Convert.ToInt32(demoMap.ZoomLevel));
  54.          DrawQuadkeys(quadKey, curLat, curLon, nextLat, nextLon);
  55.          curLon = nextLon;
  56.      }
  57.      //Reset values
  58.      curLat = minLat;
  59.      curLon = minLon;
  60.      // ‘Scan’ the map diagonally increasing both Lat & Lon in each step
  61.      while (curLon <= maxLon && curLat <= maxLat)
  62.      {
  63.          int nextLatInPixels; int nextLonInPixels;
  64.          int curLatInPixels; int curLonInPixels;
  65.          int curTileX; int curTileY;
  66.          double nextLat; double nextLon;
  67.          LatLongToPixelXY(curLat, curLon, Convert.ToInt32(demoMap.ZoomLevel), out curLatInPixels, out curLonInPixels);
  68.          nextLatInPixels = curLatInPixels + 256;
  69.          nextLonInPixels = curLonInPixels – 256;
  70.          // Now convert them back to WGS84 coords
  71.          PixelXYToLatLong(nextLatInPixels, nextLonInPixels, Convert.ToInt32(demoMap.ZoomLevel), out nextLat, out nextLon);
  72.          PixelXYToTileXY(curLatInPixels, curLonInPixels, out curTileX, out curTileY);
  73.          string quadKey = TileXYToQuadKey(curTileX, curTileY, Convert.ToInt32(demoMap.ZoomLevel));
  74.          DrawQuadkeys(quadKey, curLat, curLon, nextLat, nextLon);
  75.          curLat = nextLat;
  76.          curLon = nextLon;
  77.      }
  78.  }
  79.  ///<summary>
  80.  /// Draws the Quadkeys as rectangles on the map
  81.  ///</summary>
  82.  ///<param name=”quadkey”></param>
  83.  ///<param name=”minlat”></param>
  84.  ///<param name=”minlon”></param>
  85.  ///<param name=”maxlat”></param>
  86.  ///<param name=”maxlon”></param>
  87.  public void DrawQuadkeys(string quadkey, double minlat, double minlon, double maxlat, double maxlon)
  88.  {
  89.      //Get the Quadkeys layer
  90.      MapLayer l = (MapLayer)demoMap.FindName(“quadkeys”);
  91.      //Create polygon
  92.      LocationCollection locs = new LocationCollection();
  93.      locs.Add(new GeoCoordinate(minlat, minlon));
  94.      locs.Add(new GeoCoordinate(maxlat, minlon));
  95.      locs.Add(new GeoCoordinate(maxlat, maxlon));
  96.      locs.Add(new GeoCoordinate(minlat, maxlon));
  97.      MapPolygon mp = new MapPolygon();
  98.      mp.Locations = locs;
  99.      mp.Stroke = new System.Windows.Media.SolidColorBrush(System.Windows.Media.Colors.Red);
  100.      mp.StrokeThickness = 2;
  101.      // Add polygon
  102.      l.Children.Add(mp);
  103.      var avgLat = mp.Locations.Select(q => q.Latitude).Average();
  104.      var avgLon = mp.Locations.Select(q => q.Longitude).Average();
  105.      // Add quadkey label
  106.      TextBox qLabel = new TextBox();
  107.      qLabel.Text = quadkey;
  108.      l.AddChild(qLabel, new GeoCoordinate(avgLat, avgLon));
  109.  }

Now compile and run the app again. This time you should be able to see the tiles appearing as red rectangles every time you pan and zoom the map with the quadkey displaying inside as in the image below.

image

Hope this example clears up a few things about Bing Maps and how they work. You can download the full application from http://www.box.com/s/ppovoyfbuiqf304qod8b

Enjoy coding! – PV

Advertisements

Displaying pushpins in Bing Maps Silverlight control using GeoRSS feeds

In this post I will briefly explain how to add pushpins in the Bing Maps control using a GeoRSS feed. Although there are many ways of adding pushpins (or any geographic shape for that matter) on a Bing Maps backdrop, I have found this by far the easiest and most flexible.

So assuming you have a spatial table (in this example the table is stored in a SQL Server 2008 database, but any database could be used) containing the points you want to plot. A sample of the table is shown below:

I wont go into any details on how to create a Silverlight project and implement Bing Maps, there are plenty of good examples around. You can start here or here.  Instead, we will first focus on how to create the GeoRSS handler to retrieve the shape data from the table. To do this, right-click on your Web project and select Add->New Item->Generic Handler and name it GeoRSSHandler.ashx.

Replace the default contents in the GeoRssHandler.ashx file with the following:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Configuration;
using System.Data.SqlClient;
namespace GeoRSSAndBing.Web
{
///
/// Summary description for GeoRSSHandler
///
public class GeoRSSHandler : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
string connStr = ConfigurationManager.ConnectionStrings["spatialDB"].ToString();
context.Response.ContentType = "text/xml";
string type = context.Request["type"];
//Build the GeoRSS feed
System.Text.StringBuilder rssOutput = new System.Text.StringBuilder("");
rssOutput.AppendLine("");
rssOutput.AppendLine("");
rssOutput.AppendLine("");
rssOutput.AppendLine("POIS");
rssOutput.AppendLine("");
rssOutput.AppendLine("" + System.DateTime.Now + "");
SqlConnection conn = new SqlConnection(connStr);
conn.Open();
try
{
SqlCommand cmd = new SqlCommand();
cmd.Connection = conn;
cmd.CommandType = System.Data.CommandType.Text;
if (type == "POI")
{
//Change this statement depending on your settings
cmd.CommandText = "select Eva_Event_ID,EVA_EVENT_TYPE, EVA_EVENT_DESCRIPTION, shape.AsGml() as GeomGml from pois_wgs84";
}
//TODO: Add logic to handle lines and polygons
SqlDataReader sqlGeomRdr= cmd.ExecuteReader();
while (sqlGeomRdr.Read())
{
//Create an element for this row
rssOutput.AppendLine("");
//Set title and description
if (type == "POI")
{
rssOutput.AppendLine("" + sqlGeomRdr["EVA_EVENT_ID"].ToString() + "");
rssOutput.AppendLine("");
rssOutput.AppendLine("");
}
//TODO: Add logic to handle lines and polygons
rssOutput.AppendLine("");
//Get the geography instance GML from column 2
string gml= sqlGeomRdr["GeomGml"].ToString();
//Append the gml: prefix to all the elements due to VE parsing behavior
if (type == "POI")
{
gml = gml.Replace("<Point", "<gml:Point");
gml = gml.Replace("pos", "gml:pos");
gml = gml.Replace("</Point", "</gml:Point");
}
//TODO: Add logic to handle lines and polygons
Add the elements to the output XML
rssOutput.AppendLine(gml);
//Close and elements
rssOutput.AppendLine("");
rssOutput.AppendLine("");
}
rssOutput.Append("");
context.Response.Write(rssOutput.ToString());
}
catch (Exception ex)
{
throw ex;
}
finally
{
conn.Close();
}
}

Next, add a [Show Locations] button in your XAML such as:

<Button Cursor=”Hand” Width=”195″ Height=”25″ HorizontalAlignment=”Left” Content=”Show Locations” x:Name=”b1″ Margin=”2,10,0,1″ Click=”b1_Click”></Button> 
 
 The implementation of the Onclick event in the code behind will call the handler we just created asynchronously i.e.:
 
 private void b1_Click(object sender, RoutedEventArgs e)
 {
   Uri url = new Uri(“../GeoRSSHandler.ashx?type=POI”, UriKind.Relative);  WebClient client = new WebClient();  
    client.DownloadStringCompleted +=new DownloadStringCompletedEventHandler(client_DownloadStringCompleted); 
   client.DownloadStringAsync(url);
 } 

Finally, the downloadString Event handler code is the bit that draws the pushpins on the map:

 void client_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)

{ if (e.Error == null)

{ StringReader stream = new StringReader(e.Result); 

 XmlReader reader = XmlReader.Create(stream); 

 string gmlURI = http://www.opengis.net/gml&#8221;;map.Children.Clear();

 while (reader.Read()){

   if (reader.NodeType == XmlNodeType.Element)  {

    if (reader.NamespaceURI == gmlURI && reader.Name == reader.Prefix + “:pos”)

   {

      string[] loc = reader.ReadInnerXml().Split(” “.ToCharArray()); 

      double lat = Double.Parse(loc[1]); 

      double lon = double.Parse(loc[0]); 

      Pushpin p = new Pushpin();p.Location =new Location(lat, lon);map.Children.Add(p);
  }}}}} Next, compile and run your application. Assuming you set the initial Bing Map extent correctly, (i.e. around the area where your points are), clicking on the [Show Locations] button should display your points on Bing Maps using the Default pushpin.

The great thing with this approach, is that you can easily add logic to either add other shape types such as polygons or lines, or retrieve data from other databases by simply amending the code in the GeoRSS handler.

Silverlight interface for Mapserver GIS

Yes, such a beast does exist! And I am proud to say that Gaiocorp (ok, a bit of not-so grey advertising) has developed it and released is as an open source project in CodePlex (http://slmapviewer.codeplex.com). Lovingly called SLMapViewer, it is a reusable Silverlight map control for use with MapServer, the popular open source WebGIS. It was initially developed as  the the mapping component for some of Gaiocorp’s products and was based on the MapFlash Viewer Framework’s concept  developed by Paolo Corti. Since then it has changed almost beyond recognition, supporting a large number of ‘GIS-like’ functions, different ‘view modes’ to be used as either through a normal browser or in a street kiosk, and extendable helper classes so developers can implement their own business logic for layer display. SLMapviewer can be used as a standalone map viewer or be embedded it and thus spatially enable, ASP.NET or Silverlight applications.

Passing values between the Silverlight Bing Map Control and aspx pages

 
… or how to easily embed Bing Maps into your existing ASP.NET application. So lets assume you have your fully working ASP.NET app which contains Datagrids dataforms, etc and you now want to add that little extra spatial glitz by embedding Bing Maps. Assume also that your data contains street addresses or Latitude/Longitude pairs. The most common thing that you would want to do is to be able to zoom to the map to a point which -say- represents the location of the selected record in a datagrid and/or click along a pushpin on the map which represents a record in your database and display its attributes in the aspx page.
 
The technique I propose here is -hopefully- simple and easy to implement. Note that this is just a generic approach. Depending on your data and business logic you should be able to change it accordingly. 
It can be broadly described using the following steps:
To Zoom to the Map by clicking a button on the ASPX page:
  1. When clicking the button a hidden field in the aspx page is populated with the Lat/Lon values of the currently selected Gridview/DataForm record  
  2. The Update panel that contains the Silverlight control gets updated.
  3. This will cause the Silverlight control to get refreshed and pass through the Page_Load event
  4. The Page_Loaded event reads the value of the hidden field in the aspx page, converts them into a Bing Maps Location object and zooms the map to that location.
To display the attributes of a location by clicking on the map
  1. User clicks on the map
  2. The MouseLeftButton down event is fired (either on the map or a layer) which populates the hidden Textbox in the aspx page.
  3. The Textbox is populated with either the ID of the record or the Lat/Lon values of the click point.
  4.  The OnTextChanged() event on the textbox is fired which calls the relevant business logic function to display the attributes.
To implement the above: 
In your asp.net page make sure you enclose the Bing Maps control in an update panel as below (Note: The following code uses the Silverlight 2 control. Hasn’t been tested yet with Silverlight 3 proposed object tag but it should work.
 
 <asp:UpdatePanel ID=”upnlSL” runat=”server” UpdateMode=”Conditional”> 
<ContentTemplate>
  <div id=”BingMapInterface” style=”display:none; width:0px; position:absolute;top:-800px; “>
  <asp:TextBox ID=”txtBingMapRequests” runat=”server” Width=”100″ onblur=”javascript:Changed(this);” OnTextChanged=”txtBingMapRequests_OnTextChanged” AutoPostBack=”true”></asp:TextBox>
 </div></ContentTemplate></asp:UpdatePanel>  
 Notice that the whole update panel has been enclosed in a <div> tag with the display attribute set to none so it won’t show on the page. Also note that the txtBingMapRequests, the textbox that will receive the Lat/Lon coordinates has an onblur() event as well as a OnTextChanged event. This will ensure that a postback on the page will occur as soon as the textbox value is changed.
The onblur() javascript event is:
<script type=”text/javascript”> 

function Changed(textControl) {“upnlSL”, null);}  

</script>  
The OnTextChanged event is implemented as: 
 protected void txtBingMapRequests_OnTextChanged(object sender, EventArgs e){ 
 string  s = txtBingMapRequests.Text; 
  upnlBingMap.Update();
}
 By causing the BingMaps update panel to update the Page_Loaded function in our Silverlight project will fire which can be similar to:
void  Page_Loaded(object sender, RoutedEventArgs e)
{
//Get the value of the txtBingMapRequest textbox
HtmlElement txtRequest = HtmlPage.Document.GetElementById(“txtBingMapRequests”); 
if (txtRequest.GetProperty(“value”).ToString() != string.Empty)
{
try
{string[] coords = txtRequest.GetProperty(“value”).ToString().Split(‘;’); 
if (coords.Length == 2) {
string lon = coords[0];
string lat = coords[1]; 
Location poiLoc = new Location(Convert.ToDouble(lon), Convert.ToDouble(lat));map.SetView(poiLoc, 18);
}
}
catch (Exception){}
}
Note that we assume that the value of the textbox is a string which holds the Lat/Lon coords separated by a ‘;’
 
We can implement the reverse in a similar way so when you click on the map the aspx page gets updated with the selected location/record.