Johannes's profileHannes's Virtual Earth B...BlogListsSkyDrive Tools Help

Hannes's Virtual Earth Blog

November 20

Bing Maps Silverlight Control & Photosynth

Last week we launched a new wave of Bing Maps. My esteemed colleague Chris Pendleton has blogged about some of the changes here, here, here and here. One of the main announcements has been the launch of the Bing Maps Silverlight Control v1. You will find the interactive SDK here, the full reference on MSDN here and the download here. This control has been available as a Community Technology Preview (CTP) for quite a while but those of you who have been waiting for a fully supported version before getting involved may not be too familiar with it yet. If you did work with the CTP please be aware that it is time-bombed and will stop working by the end of the year; so please consider updating your application soon.

If you have been working with the Bing Maps AJAX Control before you may actually be missing a few things that came in handy if you wanted to display additional information like for a example a Photosynth collection. This was quite simple in the AJAX control since you could simply generate an iFrame in Photosynth and just add it to a VEShape-object using the VEShape.SetDescription-method. You can look at a sample here and download the source here).

Since pushpins in the Silverlight control don’t have an info-box like the ones in the AJAX-control the approach here is quite different but it still works very well. The major obstacle appear to be that we cannot directly integrate HTML-code into our Silverlight application. The solution is actually to use the HTML Bridge in order to lay a HTML DIV-element on top of the Silverlight control and arrange it appropriately. Of course you can write everything from the scratch but the guys from divelements have created a Silverlight-Control HtmlHost for us that you can download for free and use to add and control HTML content directly from your Silverlight application. Let’s have a crack at it. For this walkthrough we will need

First we create a new Silverlight application and add references to our Bing Maps Silverlight Control

image

In the MainPage.xaml we add a reference to our Bing Maps Silverlight control…

xmlns:m="clr-namespace:Microsoft.Maps.MapControl;assembly=Microsoft.Maps.MapControl"

 

…and the the map itself with a pushpin at the location where we our Photosynth collection is. We also attach an event to the pushpin that fires when we click on it.

<m:Map CredentialsProvider="YOUR BING MAPS KEY"
       Mode="AerialWithLabels"
       Center="32.362980181127874,-64.71483707427978"
       ZoomLevel="17">
       <m:Pushpin Name="pinMartelloTower"
                  Location="32.362980181127874,-64.71483707427978"
                  MouseLeftButtonDown="pinMartelloTower_MouseLeftButtonDown"/>
</m:Map>

 

If you had done something like this with the CTP before you will notice a few differences:

  • The namespace for the Bing Maps Silverlight Control has changed
  • We now need to provide a CredentialsProvider in order to authenticate. This is basically the Bing Maps Key that we generated before.
  • We have now a Pushpin-object that we can use as a child of a map

In my sample here I chose to bring up the Photosynth collection as part of a ChildWindow. A ChildWindow is a Silverlight control that comes with the Silverlight Toolkit. So let’s add a ChildWindow as new object to our Silverlight-project. I call it Photosynth. The trouble with a ChildWindow and HTML-DIV-elements on top is that the ChildWindow has an animation and explodes when you open it. If you would just add the HTML-DIV-element when the ChildWindow opens it would appear in the bottom-right quadrant of the screen. In order to avoid this we can simply remove the animation by changing the template in Expression Blend. In Visual Studio just right-click on the ChildWindow and select “Open in Expression Blend” from the context menu.

image

In Expression Blend right-click on the ChildWindow-object and select “Edit Template” => “Edit a Copy”.

image

Now we switch to code-view and remove the whole VisualStateManager that handles the StoryBoards for opening and closing the window. By default this would start around line 119:

image

While we’re at it we can optionally make a few more changes to adjust the design:

image

Now we can leave Expression Blend and go back to Visual Studio. In order to add the HtmlHost from divelements right-click on the Toolbox, select “Choose Items” from the context menu and add the Divelements.SilverlightTools.dll from the location where you extracted the download.

image

Now drag&drop the control on your ChildWindow. This will automatically add the required namespace you only need to add a Name-property to the HtmlHost. Your XAML-should now look like this

<controls:ChildWindow x:Class="SilverlightApplication2.Photosynth"
           xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
           xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
           xmlns:controls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls"
           xmlns:my="clr-namespace:Divelements.SilverlightTools;assembly=Divelements.SilverlightTools"  
           Width="400" Height="300"
           Title="Photosynth">
    <controls:ChildWindow.Resources>
…
</controls:ChildWindow.Resources
> <controls:ChildWindow.Style> <StaticResource ResourceKey="ChildWindowStyle"/> </controls:ChildWindow.Style>
<Grid x:Name="LayoutRoot" Margin="2"> <Grid.RowDefinitions> <RowDefinition /> <RowDefinition Height="Auto" /> </Grid.RowDefinitions> <my:HtmlHost Name="htmlHost"></my:HtmlHost> </Grid> </controls:ChildWindow>

Finally we can start coding. The only code we need is actually in the MainPage.xaml.vb. When we initialize the page we also add a handler which resizes the ChildWindow whenever the size of the browser window changes.

When we click on the pushpin we set the title for the ChildWindow as well as the source for the HtmlHost. In this case we just add the iFrame which we can copy directly from Photosynth…

image

…and make to minor changes:

  • we change the width and height of the iFrame to 100%
  • we set the URL-parameter delayLoad from true to false. By default the Photosynth viewer would show a static image and only load the collection after you clicked on this image. By changing this parameter we load the collection immediately.
Imports System.Windows.Browser

Partial Public Class MainPage
    Inherits UserControl

    Public myHeight As Integer
    Public myWidth As Integer

    Private WithEvents cwPhotosynth As New Photosynth

    Public Sub New()
        InitializeComponent()

        AddHandler LayoutRoot.SizeChanged, AddressOf Page_SizeChanged
    End Sub

    Private Sub Page_SizeChanged(ByVal sender As Object, ByVal e As SizeChangedEventArgs)
        myWidth = CInt(e.NewSize.Width)
        myHeight = CInt(e.NewSize.Height)

        cwPhotosynth.Width = e.NewSize.Width * 0.8
        cwPhotosynth.Height = e.NewSize.Height * 0.8
    End Sub

    Private Sub pinMartelloTower_MouseLeftButtonDown _
(ByVal sender As System.Object, ByVal e As System.Windows.Input.MouseButtonEventArgs) cwPhotosynth.Title =
"Martello Tower" cwPhotosynth.htmlHost.SourceHtml = _
"<iframe frameborder='0' src='http://photosynth.net/embed.aspx?cid=ba12ab48-6899-4d7f-b28c-624f5f7ff4f0” + _
&delayLoad=false&slideShowPlaying=false' width='100%' height='100%'></iframe>"
cwPhotosynth.Show()
End Sub End Class

Well that’s already it:

image

You can have a look at the sample here and download the source code here.

Note: if you download the sample code you need to create a Bing Maps Key and enter it into the XAML in order to load the Bing Maps. Also note that the solution was build with Visual Studio 2010 Beta 2, however all methods work as well in Visual Studio 2008 SP1

November 13

Bing Maps at TechEd Europe

On behalf of our friends from Borchert GeoInfo and the Bing Maps team I would like to thank everybody who visited us at the booth during the TechEd in Berlin or attended one of my sessions. Some of you asked for my presentations and the sample code. You will find everything here on my SkyDrive:

Have a look at the Readme.pdf if you are unsure about what you need.

November 08

Bing Maps at TechEd Europe

Tomorrow the TechEd Europe opens it’s gates in Berlin and Bing Maps will be represented as well. Chris Pendleton will come over from Redmond and join us for the week and I have the privilege of presenting 2 sessions on Bing Maps:

  1. WIA02-IS Bing Maps Silverlight Control, Location Intelligence, and Microsoft SQL Server 2008
    Tue 10th of November 13:30-14:45 Interactive Theatre 2 – Orange
  2. WIA306 Enhancing the Mapping Experience with Microsoft Bing Maps
    Thu 12th of November 17:00-18:15 New York 3 - Hall 7-1a

Here is a quick Bing Maps Collection with some MapCruncher layers that shows the venue and helps you find the Bing Maps booth and the locations for my sessions :-) You can also jump straight into a 3D-tour.

image

I’m looking forward to see some of you in Berlin.

September 02

Bing Maps & SQL Server 2008 R2 Reporting Services

SQL Server 2008 R2 is the next generation of the Microsoft SQL Server platform. The release is planned for the first half of calendar year 2010 but for those who can’t wait there is as always a community technology preview (CTP). The August CTP has lots of new features and from the mapping perspective the most interesting one is the integration of a maps in SQL Server Reporting Services. With just a few mouse-clicks you can generate thematic maps from spatial data stored as GEOMTRY or GEOGRAPHY data types in SQL Server 2008 or from ESRI SHP-files and you can use Bing Maps roads, aerial or hybrid images as background data.

If you are as nosy as me, you can download the CTP here. Check it out it’s really incredible simple.

image

August 21

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:

 

Visitors

The Latest News From

Chris Pendleton

Loading...Loading...

Richard Brundritt

Loading...Loading...

VE3D-Team

Loading...Loading...

Ed Katibah

Loading...Loading...