Rendering Static Virtual Earth Bird’s Eye Images

It recently occurred to me that there is a misunderstanding within the Virtual Earth Web Service SDK that may lead you to believe that you could set the MapStyle enumeration to get a Bird’s eye image. Not the case. This is part of the method for getting tiles (not an actual map). So, via the Virtual Earth Web Service you have access to all of our tiles – road, aerial, hybrid and Bird’s eye, and in desktop and mobile flavors – and can make your own mapping experiences with them. This post will show you how to get the Bird’s eye tiles and create a single stitched image for displaying a single, static image, inserting Bird’s eye images into documents, showing off to your friends, etc., etc.

image

The following application will allow a user to enter an address, geocode the address, take the coordinates and request the Bird’s eye map tiles via VEWS, then stream a new image out of the tiles for rendering to the client. You will need to add references to the Token Service, Imagery Service and Geocode Service for this application. You will also need to enter your username and password for your developer or customer account in order to access the Virtual Earth Web Services. And, for those of you non-coders, I posted this application to my Windows Azure Cloudapp site.

//VEWSX.GetBirdsEyeImage.aspx.cs

using System;
using System.Collections;
using System.Configuration;
using System.Data;
using System.Web;
using System.Web.Security;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using VEWSX.GeocodeService;
using VEWSX.ImageryService;

namespace VEWSX
{
public partial class GetBirdsEyeImage : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{

        }

        private string GetToken()
{
// Set Virtual Earth Platform Developer Account credentials to access the Token Service
TokenService.CommonService commonService = new TokenService.CommonService();
commonService.Credentials = new System.Net.NetworkCredential("[USERNAME]", "[PASSWORD]");

            // Set the token specification properties
TokenService.TokenSpecification tokenSpec = new TokenService.TokenSpecification();

            tokenSpec.ClientIPAddress = Page.Request.UserHostAddress.ToString();
tokenSpec.TokenValidityDurationMinutes = 60;

            string token = "";

            // Get a token
try
{
token = commonService.GetClientToken(tokenSpec);
}
catch (Exception ex)
{
token = "";
}

            return token;
}

        protected void Button1_Click(object sender, EventArgs e)
{
string strLocation;
string statusMessage = "";
int zoomlevel = 20;
Bitmap dest = null;
int orientationInDegrees = 0;
string orientation = "N";

            //Get Address, Zoom and Orientation from URL
strLocation = TextBox1.Text;

            orientation = RadioButtonList1.SelectedValue;
orientation = orientation.ToUpper();
if (orientation == "E")
orientationInDegrees = 90;
else if (orientation == "S")
orientationInDegrees = 180;
else if (orientation == "W")
orientationInDegrees = 270;

            //Zoom = 1 or 2
string tmp = RadioButtonList2.SelectedValue;
zoomlevel = int.Parse(tmp);
if (zoomlevel == 1)
zoomlevel = 18;
else if (zoomlevel == 2)
zoomlevel = 20;

            //get VEWS Auth Token
string token = GetToken();

            if (token == "")
{
statusMessage = "Issue obtaining VEWS Token";
}
else
{
try
{
GeocodeService.GeocodeRequest geocodeRequest = new GeocodeService.GeocodeRequest();

                    // Set the credentials using a valid Virtual Earth token
geocodeRequest.Credentials = new GeocodeService.Credentials();
geocodeRequest.Credentials.Token = token;

                    // Set the full address query
geocodeRequest.Query = strLocation;

                    // Set the options to only return high confidence results
GeocodeService.ConfidenceFilter[] filters = new GeocodeService.ConfidenceFilter[1];
filters[0] = new GeocodeService.ConfidenceFilter();
filters[0].MinimumConfidence = GeocodeService.Confidence.High;

                    GeocodeService.GeocodeOptions geocodeOptions = new GeocodeService.GeocodeOptions();
geocodeOptions.Filters = filters;

                    geocodeRequest.Options = geocodeOptions;

                    // Make the geocode request
GeocodeService.GeocodeServiceClient geocodeService = new GeocodeService.GeocodeServiceClient();
GeocodeService.GeocodeResponse geocodeResponse = geocodeService.Geocode(geocodeRequest);

                    GeocodeService.ResponseStatusCode rc = geocodeResponse.ResponseSummary.StatusCode;

                    if (geocodeResponse.Results.Length > 0)
{
double latitude = geocodeResponse.Results[0].Locations[0].Latitude;
double longitude = geocodeResponse.Results[0].Locations[0].Longitude;

                        //Start Birds Eye Stuff
var imageryService = new ImageryServiceClient();

                        var request = new ImageryMetadataRequest
{
Style = ImageryService.MapStyle.Birdseye,
Options = new ImageryMetadataOptions
{
Location = new ImageryService.Location { Latitude = latitude, Longitude = (longitude) },
ZoomLevel = zoomlevel,
Heading = new ImageryService.Heading { Orientation = orientationInDegrees }
},
Credentials = new ImageryService.Credentials { Token = token },
UserProfile = new ImageryService.UserProfile { DeviceType = ImageryService.DeviceType.Desktop }
};

                        var metaDataReturn = imageryService.GetImageryMetadata(request);

                        var result = (ImageryMetadataBirdseyeResult)metaDataReturn.Results[0];

                        //Make a bitmap big enough for the entire BE Scene
dest = new Bitmap(result.TilesX * 256, result.TilesY * 256, PixelFormat.Format32bppPArgb);

                        System.Net.WebClient c = new System.Net.WebClient();
Bitmap src;
Graphics g;
string imagepath;

                        //put the tiles back together in a large bitmap for the scene
for (int y = 0; y < result.TilesY; y++)
{
for (int x = 0; x < result.TilesX; x++)
{
imagepath = result.ImageUri.Replace("{tileId}", (y * result.TilesX + x).ToString()).
Replace("{subdomain}", result.ImageUriSubdomains[0]).
Replace("{token}", token);

                                //Get a SRC image tile from URL
src = new Bitmap(c.OpenRead(imagepath));

                                g = Graphics.FromImage(dest);
g.DrawImage(src, new Point(x * 256, y * 256));
}
}

                        c.Dispose();
c = null;
}
else
{
statusMessage = "Couldn't Find your address";
}
}
catch (Exception ex)
{
statusMessage = "Doh! Something went wrong. " + ex.ToString();
}
}

            //Return the Image or an Error Message
if (statusMessage != "")
{
//Return Error Message
Response.Buffer = true;
Response.Clear();
Response.ContentType = "text/plain";
Response.Write(statusMessage);
Response.End();
}
else
{
//Return the Bitmap
Response.Buffer = true;
Response.Clear();
Response.ContentType = "image/jpeg";
dest.Save(Response.OutputStream, ImageFormat.Jpeg);
Response.End();
}
}
}
}

//VEWSX.GetBirdsEyeImage.aspx

    <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="GetBirdsEyeImage.aspx.cs" Inherits="VEWSX.GetBirdsEyeImage" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="https://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
<title>Getting Static Birds eye Images Using Microsoft's Virual Earth Web Service</title>
<!-- Author: Chris Pendleton -->
</head>
<body>
<form id="form1" runat="server">
<div>
Render Static Bird's Eye Images from Microsoft&#39;s Virtual Earth Web Service<br />
</div>
Enter Address:
<asp:TextBox ID="TextBox1" runat="server" Width="342px"></asp:TextBox>
<br />
<asp:Label ID="Label3" runat="server" Text="Set Orientation"></asp:Label>
<asp:RadioButtonList ID="RadioButtonList1" runat="server" Height="23px"
Width="138px">
<asp:ListItem Selected="True" Value="N">North</asp:ListItem>
<asp:ListItem Value="E">East</asp:ListItem>
<asp:ListItem Value="S">South</asp:ListItem>
<asp:ListItem Value="W">West</asp:ListItem>
</asp:RadioButtonList>
<asp:Label ID="Label2" runat="server" Text="Set Zoom Level"></asp:Label>
<asp:RadioButtonList ID="RadioButtonList2" runat="server" Height="61px" Width="228px">
<asp:ListItem Selected="True" Value="18">Zoom Level 1 (18)</asp:ListItem>
<asp:ListItem Value="20">Zoom Level 2 (20)</asp:ListItem>
</asp:RadioButtonList>
<br />
<asp:Button ID="Button1" runat="server" onclick="Button1_Click"
Text="Get Bird's Eye Image" />
<br />
</form>
</body>
</html>

So, go generate some Bird’s eye images already.

CP