Developing with Virtual Earth Web Services

Need a mapping solution for your application?  Why not try out Virtual Earth.  They just released the ability to do with Web Services on top of their normal offering. 

Shortly I’ll integrate this in with a mobile client, however I’m sharing the code for a Win32 client to help out early adopters.  You can download my source for this over at Peace Love Code.  The solution has both VB and C#.  All you need to do is get an account for the account information needed.

image image

Where to start?  Lets knock on the door.

With most web services, you need an account.  Here is the process for Virtual Earth:

  1. Go to https://mappoint-css.live.com/mwssignup and sign up with a Windows Live ID
  2. You’ll receive an email, click the link to confirm your sign up.
  3. Get a cup of coffee to wait for another email.
  4. You’ll receive another email with additional infromation.  The big one is the URL for the VE Platform Customer Service Site (VEP CSS)
    https://mappoint-css.live.com/CSCV3/ go there.
  5. Set up your password.

Account secure, now on to Visual Studio.  We’ll have to add in the web service for the token.  If you don’t know how to do this, here are the steps in Visual Studio 2008.

  1. Go to Project->Add Web Reference
  2. Use https://staging.common.virtualearth.net/find-30/common.asmx as your URL.
    image 
  3. You’ll be prompted for a login and password, use your VE credentials for this.  This is not your Live ID.
  4. Click Ok

Now that we have the Common Service, we’ll look at the code to get the token from the service.  First we’ll have to make a reference to the web service by having a using / import statement.  And we’ll create a Authentication class.  Since I’ll be using a Win32 client, I’ll have to go about a different way about getting my IP.

C#:

public class Authentication
{
    public static string strVEWSToken;

    public static string Authenticate()
    {
        // for web pages
        // Page.Request.UserHostAddress
        var ipToken = "127.0.0.1";
        var ips = Dns.GetHostEntry(Dns.GetHostName()).AddressList;
        foreach (var ip in ips)
        {
            if (ip.AddressFamily != System.Net.Sockets.AddressFamily.InterNetwork)
                continue;

            // got a valid IPv4  address
            ipToken = ip.ToString();
            break;
        }

        return Authenticate(ipToken);
    }

    public static string Authenticate(string strIP)
    {
        var commonService = new CommonService
            {
                Url = "https://staging.common.virtualearth.net/find-30/common.asmx",
                Credentials = new NetworkCredential("SomeNumber", "SomePassword")
            };

        // Create the TokenSpecification object to pass to GetClientToken. 
        var tokenSpec = new TokenSpecification
            {
                // Use the Page object to retrieve the end-client’s IPAddress. 
                ClientIPAddress = strIP,
                // The maximum allowable token duration is 480 minutes (8 hours). 
                // The minimum allowable duration is 15 minutes. 
                TokenValidityDurationMinutes = 480
            };

        // Now get a token from the Virtual Earth Platform Token service. 
        strVEWSToken = commonService.GetClientToken(tokenSpec);
        return strVEWSToken;
    }
}

vb.net:

Public Class Authentication
    Public Shared strVEWSToken As String

    Public Shared Function Authenticate() As String
        ' for web pages
        ' Page.Request.UserHostAddress
        Dim ipToken As String = "127.0.0.1"
        Dim ips As var = Dns.GetHostEntry(Dns.GetHostName()).AddressList
        For Each ip As IPAddress In ips
            If ip.AddressFamily <> System.Net.Sockets.AddressFamily.InterNetwork Then
                Continue For
            End If

            ' got a valid IPv4  address
            ipToken = ip.ToString()
            Exit For
        Next

        Return Authenticate(ipToken)
    End Function

    Public Shared Function Authenticate(ByVal strIP As String) As String
        Dim commonService As New CommonService()
        commonService.Url = "https://staging.common.virtualearth.net/find-30/common.asmx"
        commonService.Credentials = New NetworkCredential("SomeNumbers", "YourPassword")

        ' Create the TokenSpecification object to pass to GetClientToken. 
        Dim tokenSpec As New TokenSpecification()

        ' Use the Page object to retrieve the end-client’s IPAddress. 
        tokenSpec.ClientIPAddress = strIP
        ' The maximum allowable token duration is 480 minutes (8 hours). 
        ' The minimum allowable duration is 15 minutes. 
        tokenSpec.TokenValidityDurationMinutes = 480

        ' Now get a token from the Virtual Earth Platform Token service. 
        strVEWSToken = commonService.GetClientToken(tokenSpec)
        Return strVEWSToken
    End Function
End Class

You have a token?  Come in, Come in!

With a token, now we’ll be able to some querying.  For this example, we’ll just fetch imagery, however you can get geographical data, routing and searching data following a similar route taken in this example.  To find out where to get these WDSLs and more about it, MSDN has a full list along with a detailed break down.

For this example, we’ll add a service reference to https://staging.dev.virtualearth.net/webservices/v1/imageryservice/imageryservice.svc.  You can either go Project->Add Service Reference or Add Web Reference.  I used a service reference for this example.  I also named it ImageryService as I’m extremely creative. 

This example’s primary goal is to show how to get bird’s eye data back.  For that, we’ll have to use GetImageryMetadata.  If you just want overhead data with or without roads overlaid, you can use GetMapUri instead.

A quick UX design.

imageI have crazy user interface skills (typically me calling up Jeff for a pow-wow).  I have a quick and dirty interface here for getting some needed information.

  1. With Bird’s eye, you can shift your orientation.
  2. Setting what view you want.
  3. What zoom level, 1 to 21.
  4. What is your device, mobile or desktop

You’ll note I left off latitude and longitude, that is this was just a demo application, I wanted to get up and running quick so I’ve hardcoded my latitude and longitude to Forest Park in St. Louis, MO.

To populate the combo boxes, we’ll use a quick trick.  We’ll use Enum.GetValues to set the data source of the combo boxes.  To fetch the data, we’ll use SelectedValue on the combo box and cast to the needed data type.

C#:

cmbView.DataSource = Enum.GetValues(typeof(MapStyle));
cmbDeviceType.DataSource = Enum.GetValues(typeof(DeviceType));

vb.net:

cmbView.DataSource = [Enum].GetValues(GetType(MapStyle))
cmbDeviceType.DataSource = [Enum].GetValues(GetType(DeviceType))

Making the secret knock for images

After getting what the user wanted, we need to do a request.

C#:

var imageryService = new ImageryServiceClient();

var request = new ImageryMetadataRequest
      {
        Style = mapStyle,
          Options = new ImageryMetadataOptions
                        {
                            Location = new Location {Latitude = 38.637593, Longitude = (-90.270843)},
                            ZoomLevel = zoomLevel,
                            Heading = new Heading {Orientation = orientation}
                        },
          Credentials = new Credentials {Token = token},
          UserProfile = new UserProfile {DeviceType = deviceType}
      };

var metaDataReturn = imageryService.GetImageryMetadata(request);

vb.net:

Dim imageryService As New ImageryServiceClient()

Dim request As New ImageryMetadataRequest()
request.Style = mapStyle
request.Options = New ImageryMetadataOptions()
request.Options.Location = New Location()
request.Options.Location.Latitude = 38.637593
request.Options.Location.Longitude = (-90.270843)
request.Options.ZoomLevel = zoomLevel
request.Options.Heading = New Heading()
request.Options.Heading.Orientation = orientation
request.Credentials = New Credentials()
request.Credentials.Token = token
request.UserProfile = New UserProfile()
request.UserProfile.DeviceType = deviceType

Dim metaDataReturn As ImageryMetadataResponse = imageryService.GetImageryMetadata(request)

Data Fetched but we need a full out URI

We have our data but depending on the source, we’ll have to do some additional transformation.  Depending on if you do a overhead or bird eye view, you’ll have to manipulate the URI to include subdomain, culture, and tile id.  Once again, we’re assuming a fat client application.  For a website, we’d grab the culture information differently just like we did with the IP.

C#:

private static string createBirdEyeUri(string imageUri, int tileId, string[] subdomain, string token)
{
    //{tileId}, {subdomain}, {token}
    if (subdomain.Length > 0)
        imageUri = imageUri.Replace("{tileId}", tileId.ToString()).
            Replace("{subdomain}", subdomain[0]).
            Replace("{token}", token);

    return imageUri;
}

private static string createOverheadUri(string imageUri, string token)
{
    //{culture}, {token}
    imageUri = imageUri.Replace("{culture}", System.Threading.Thread.CurrentThread.CurrentCulture.IetfLanguageTag).
            Replace("{token}", token);

    return imageUri;
}

vb.net:

Private Shared Function createBirdEyeUri(ByVal imageUri As String, ByVal tileId As Integer, ByVal subdomain As String(), ByVal token As String) As String
    '{tileId}, {subdomain}, {token}
    If subdomain.Length > 0 Then
        imageUri = imageUri.Replace("{tileId}", tileId.ToString()).Replace("{subdomain}", subdomain(0)).Replace("{token}", token)
    End If

    Return imageUri
End Function

Private Shared Function createOverheadUri(ByVal imageUri As String, ByVal token As String) As String
    '{culture}, {token}
    imageUri = imageUri.Replace("{culture}", System.Threading.Thread.CurrentThread.CurrentCulture.IetfLanguageTag).Replace("{token}", token)

    Return imageUri
End Function

Laying down the tiles, don’t forget the grout

For this example, we’ll do just bird eye as it is slightly more complex.  We’ll have to create a tile layout and programmatically add in the tiles since we don’t know how many will be there.

c#:

var result = (ImageryMetadataBirdseyeResult)metaDataReturn.Results[0];
var pics = new PictureBox[result.TilesX * result.TilesY];

int currentTop = 0;
int currentLeft = 0;

for (int y = 0; y < result.TilesY; y++)
{
    for (int x = 0; x < result.TilesX; x++)
    {
        int currentIndex = y * result.TilesX + x;
        pics[currentIndex] = new PictureBox
            {
                Top = currentTop,
                Left = currentLeft,
                Width = metaDataReturn.Results[0].ImageSize.Width,
                Height = metaDataReturn.Results[0].ImageSize.Height,
                ImageLocation =
                    createBirdEyeUri(result.ImageUri, currentIndex, result.ImageUriSubdomains, token)
            };

        currentLeft += metaDataReturn.Results[0].ImageSize.Width;
    }
    currentTop += metaDataReturn.Results[0].ImageSize.Height;
    currentLeft = 0;
}

gbImagery.Controls.AddRange(pics);

vb.net:

Dim result As ImageryMetadataBirdseyeResult = DirectCast(metaDataReturn.Results(0), ImageryMetadataBirdseyeResult)
Dim pics As PictureBox() = New PictureBox(result.TilesX * result.TilesY) {}

Dim currentTop As Integer = 0
Dim currentLeft As Integer = 0

Dim y As Integer = 0
While y < result.TilesY
    Dim x As Integer = 0
    While x < result.TilesX
        Dim currentIndex As Integer = y * result.TilesX + x
        pics(currentIndex) = New PictureBox()
        pics(currentIndex).Top = currentTop
        pics(currentIndex).Left = currentLeft
        pics(currentIndex).Width = metaDataReturn.Results(0).ImageSize.Width
        pics(currentIndex).Height = metaDataReturn.Results(0).ImageSize.Height
        pics(currentIndex).ImageLocation = createBirdEyeUri(result.ImageUri, currentIndex, result.ImageUriSubdomains, token)


        currentLeft += metaDataReturn.Results(0).ImageSize.Width
        System.Math.Max(System.Threading.Interlocked.Increment(x),x - 1)
    End While
    currentTop += metaDataReturn.Results(0).ImageSize.Height
    currentLeft = 0
    System.Math.Max(System.Threading.Interlocked.Increment(y),y - 1)
End While

gbImagery.Controls.AddRange(pics)

Go Do’s!

For more information and examples about Virtual Earth and their services, visit the Virtual Earth blog on MSDN.  You have the ability to play with geographical, routing, and searching data on top of just the extreme high resolution earth data.

You can download my source for this over at Peace Love Code.  The solution has both VB and C#.  All you need to do is get an account for the account information needed.

Mark Brown Sep 29, 2008 @ 12:17 AM

# you rock man
Clint, you kick ass man. I'm linking you from my blog.

SoulSolutions Sep 29, 2008 @ 1:43 AM

# re: Developing with Virtual Earth Web Services
Great article, interesting that i couldn't find any reference to having to replace the tokens in the URI's, even Chris' first post missed that.

So if anyone finds that their valid credentials don't seem to allow the web reference to be added to the VS2008 project, keep an eye on the URL in the dialog, for me it actually asked for my credientials 4 times. I kept canceling after the first box thinking it wasn't working. Keep at it and it will eventually add.
Also for people with older accounts like mine if you get an error accessing staging try the production service. For example i can't get to staging.
John.

SoulSolutions Sep 29, 2008 @ 9:42 PM

# re: Developing with Virtual Earth Web Services
If anyone is getting stuck on setting up the initial references I put together a little 10min video:
http://www.liveside.net/developer/archive/2008/09/29/video-getting-started-with-the-virtual-earth-web-service.aspx

segment Oct 8, 2008 @ 4:19 AM

# re: Developing with Virtual Earth Web Services
hi, I've received token successfully. But when I called "var metaDataReturn = imageryService.GetImageryMetadata(request);" Some occur happened as belows:


The message with Action 'http://dev.virtualearth.net/webservices/v1/IImageryService/GetImageryMetadata' cannot be processed at the receiver, due to a ContractFilter mismatch at the EndpointDispatcher. This may be because of either a contract mismatch (mismatched Actions between sender and receiver) or a binding/security mismatch between the sender and the receiver. Check that sender and receiver have the same contract and the same binding (including security requirements, e.g. Message, Transport, None).

PS: I can't add web references "
https://staging.common.virtualearth.net/find-30/common.asmx" in VS2008 successfully, but I can't receive token using this program

any suggestions? thx

Clint Rutkas Oct 8, 2008 @ 11:01 AM

# re: Developing with Virtual Earth Web Services
@segment I'll contact you directly on top of this response.

If you can't receive a token, you can't use VEWS so I'm not sure how you pulled that off. You'll be prompted by a user name / password field when you attempt to add it as a web service about 4 to 5 times. You'll want to just keep cranking in your staging cred's which are a number then the strong password you gave them.

Rubicon Oct 21, 2008 @ 8:51 AM

# re: Developing with Virtual Earth Web Services
To reference the service you should use
http://staging.dev.virtualearth.net/webservices/v1/metadata/imageryservice/imageryservice.wsdl
:-)

Daniel Oct 29, 2008 @ 11:52 AM

# re: Developing with Virtual Earth Web Services
I just created an account but I am unable to set the password!
I get a message stating that I need to use a strong password even when I enter a password like this 8D75JeYB9QnCK3NzpfMb - Is this page broken? I really need to start using the staging API as I am trying to prototype a design!

Clint Rutkas Nov 1, 2008 @ 11:10 AM

# re: Developing with Virtual Earth Web Services
@Daniel: email me so I can get better information to help you out

Clint Rutkas Nov 20, 2008 @ 2:44 PM

# re: Developing with Virtual Earth Web Services
If you go to https://mappoint-css.live.com/CSCV3/Css.aspx and then click the Verify Credentials does your login / password work? It is NOT your Windows Live ID

Need more help Jan 23, 2009 @ 5:14 AM

# re: Developing with Virtual Earth Web Services
Hi, I am developing the same in silverlight application. But, Iam not getting method "GetImageryMetadata", instead GetImageryMetadataAsyc and GetImageryMetadataCompleted event. I am using this event and the results im getting as ImageryMetadataResponse class. So iam not getting tiles properties. Actually I am able to display the image but i want to do more like zoom and finding location in silverlight application. Could you/anyone please help me out in this.

Thanks in advance.
my mail id: ramanaiah97@gmail.com

plase send me samples to this id if possible.

Gul Oct 13, 2014 @ 2:51 AM

# re: Developing with Virtual Earth Web Services
the link is down or some other problem ..its not opening

Post a Comment

Please add 8 and 6 and type the answer here: