Bing Maps & Wikipedia

If you have been using the “Explore Collections” feature in the consumer facing implementation of Bing Maps before you may have wondered if it is possible to get this feature into your own Bing Maps implementation as well. Indeed that is possible and there is a quite simple approach. In the following walkthrough we will get specifically Wikipedia content into our Bing Maps.

Let’s start with a closer look how the consumer side does it:

If we go to Bing Maps and search for a location like “Tower of London” we’ll find that we can explore collections for this location.

image

These collections are basically a whole lot of community content that was created in Bing Maps collections, is available as GeoRSS, KML, KMZ or GPX on the internet and was found by the crawlers or is integrated from Wikipedia and Photosynth. We can filter this content, apply different sort criteria such as distance and then we could subscribe to an RSS-feed with the results.

image

A closer look at the RSS-feed will show that it is in fact a GeoRSS-feed and we know of course that we can import GeoRSS-feeds into Bing Maps using the VEMap.ImportShapeLayerData-method.

image

What we really want is however not a static feed, we want to update the results when we pan or zoom the map so let’s have a closer look at the URL of the feed:

http://www.bing.com/maps/GeoCommunity.asjx?action=retrieverss&mkt=en-gb&ss=&bbox=-1.0073968023061534,51.42229465956134,-0.8444901555776242,51.50004927438254&startindex=0&order=distance&tag=Wikipedia

So in fact we are calling a web service that generates the GeoRSS-feed dynamically and the parameter bbox contains the bounding box with the South-West and North-East corner of the area for which we want to retrieve the data. Well that is simple enough to implement but there is one more thing to consider: If we call a GeoRSS-feed that is in a different domain we get an annoying security warning from our browser:

image

To avoid this security warning we can set up a proxy as described by Mike McDougall here.

Our HTML- and JavaScript code could look like 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://ecn.dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=6.2"></script>
      <script type="text/javascript">
          var map = null;

          //VEShapeLayer
          var slGeoRSS = new VEShapeLayer();

          function GetMap() {
              map = new VEMap('myMap');
              map.LoadMap(new VELatLong(51.508145,-0.07626), 17, 'h', false);
          }

          function AddShape(control) {
              if (document.getElementById(control).checked == false) {
                  //Delete all Shapes
                  slGeoRSS.DeleteAllShapes();

                  //Detach Map-Events
                  map.DetachEvent("onendpan", LoadData);
                  map.DetachEvent("onendzoom", LoadData);
              }
              else {
                  //Attach Map-Events
                  map.AttachEvent("onendpan", LoadData);
                  map.AttachEvent("onendzoom", LoadData);
                  LoadData();
              }
          }

          function LoadData() {
              map.DeleteAllShapes();

              //Retrieve the boundaries of the mapview
              var nePixel = new VEPixel(600, 0); //North-East corner of the map view
              var swPixel = new VEPixel(0, 400); //South West corner of the map view
              var neLatLon = map.PixelToLatLong(nePixel);
              var neLat = neLatLon.Latitude;
              var neLon = neLatLon.Longitude;
              var swLatLon = map.PixelToLatLong(swPixel);
              var swLat = swLatLon.Latitude;
              var swLon = swLatLon.Longitude;

              //Build URL to call the server
              var url = "./GeoRSS-Proxy.ashx?source=http://www.bing.com/maps/GeoCommunity.asjx?";
              url += "action=retrieverss&mkt=en-gb&ss=&bbox=";
              url += swLon + ",";
              url += swLat + ",";
              url += neLon + ",";
              url += neLat;
              url += "&startindex=0&order=distance&tag=Wikipedia";

              var veLayerSpec = new VEShapeSourceSpecification(VEDataType.GeoRSS, url, slGeoRSS);
              map.ImportShapeLayerData(veLayerSpec, onGeoRSSLoad, false);
          }

          function onGeoRSSLoad(a, b) {
              var numShapes = slGeoRSS.GetShapeCount();
              var numPoints = 0;
              for (var i = 0; i < numShapes; ++i) {
                  var s = slGeoRSS.GetShapeByIndex(i);
                  s.SetCustomIcon("IMG/wikipedia.gif");
              }
          }
      </script>
   </head>
   <body onload="GetMap();">
      <div id='myMap' style="position:absolute; top:0px; left:0px; width:600px; height:400px;"></div><br />
      <div id='divCtrl' style="position:absolute; top:400px; left:0px; width:600px;" >
        <input id="cbGeoRSS" type="checkbox" onclick="AddShape('cbGeoRSS')" /><a>Wikipedia</a><br />
      </div>
   </body>
</html>

And here is the proxy implemented as a Generic WebHandler

<%@ WebHandler Language="VB" Class="GeoRSS_Proxy" %>

Imports System
Imports System.Web
Imports System.Net
Imports System.IO

Public Class GeoRSS_Proxy : Implements IHttpHandler
    
    Public Sub ProcessRequest(ByVal context As HttpContext) Implements IHttpHandler.ProcessRequest
        Dim myUrl As String = ""
        myUrl = context.Request.QueryString(0)
        For i = 1 To context.Request.QueryString.Count - 1
            myUrl = myUrl + "&" + context.Request.QueryString.AllKeys(i) + "=" + context.Request.QueryString(i)
        Next
        'Dim source As String = context.Request.QueryString("source")
        context.Response.ContentType = "text/xml"
        context.Response.ContentEncoding = System.Text.Encoding.UTF8

        Dim request As HttpWebRequest = DirectCast(HttpWebRequest.Create(myUrl), HttpWebRequest)
        Dim response As HttpWebResponse = DirectCast(request.GetResponse(), HttpWebResponse)
        Dim stream As StreamReader = New StreamReader(response.GetResponseStream(), Encoding.ASCII)
        context.Response.Write(stream.ReadToEnd())
    End Sub
 
    Public ReadOnly Property IsReusable() As Boolean Implements IHttpHandler.IsReusable
        Get
            Return False
        End Get
    End Property

End Class

image

The sample code is available here:

Advertisements
This entry was posted in Bing Maps. Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s