Geocoding with the Virtual Earth Web Service


For those of you who may want to get access to the 85 million unique rooftop or parcel centroid accurate points we’ve aggregated in the Virtual Earth Web Service (VEWS), I figured it was only fair that I post a little something to help move you along. Hopefully, this helps you get past a sticking point in your application development. Also, I did all of this on staging so everyone who has an account can do this.

Okay, so, first things first, you need to authenticate – see my post Authentication and Tokens with Virtual Earth for authentication. You’ll need a token to use any part of the VEWS and there’s a good explanation on how to do that for VEWS in my other post (and information on getting a map) – Getting a Map with the Virtual Earth Web Service, but I’ll copy the code to be nice.

The application consists of 3 files – Authentication.cs (a class file for getting me a token); Geocoding.aspx (the UI file for adding web parts); and, Geocoding.aspx.cs (the code behind file where the magic happens). As for my code, it’s only meant to get you up and running to understand architecture – it is not production code. You’ll want error handling and try catches and all the good stuff – this is just the meat and potatoes.

Authentication.cs

using System;
using System.Data;
using System.Configuration;
using VEWSStaging; //VEWS is a reference to http://staging.common.virtualearth.net/find-30/common.asmx

/// <summary>
/// Authenticating for VE Tokens
/// </summary>
public class Authentication
{
    public static string strVEWSToken;
    public static string Authenticate(string strIP)
    {
        CommonService commonService = new CommonService();
        commonService.Url = "https://staging.common.virtualearth.net/find-30/common.asmx";
        commonService.Credentials = new System.Net.NetworkCredential("XXXX", "YYYY");

        // Create the TokenSpecification object to pass to GetClientToken.
        TokenSpecification tokenSpec = 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;
    }
}

Next, I’ll need to set a reference to the geocoding service (WCF) – Website | Add Service Reference. I named my service "GeocodeService" which you can see has two methods – Geocode and ReverseGeocode.

image 

Next, we’ll need to add UI elements to Geocoding.aspx. I added a Label (as a title), a Text box (to input addresses or places), a Button (to submit the data) and a Table (to store the results). When I’m done, Geocoding.aspx looks like this…

Geocoding.aspx

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Geocoding.aspx.cs" Inherits="Geocoding" %>

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

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>CP’s Geocoding Page</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        Enter a location</div>
    <asp:TextBox ID="TextBox1" runat="server" Height="20px" Width="375px"></asp:TextBox>
    <asp:Button ID="Button1" runat="server" Height="21px" onclick="Button1_Click"
        Text="Geocode!" Width="80px" />
    <asp:Table ID="Table1" runat="server" Height="475px" style="margin-top: 0px"
        Width="500px">
    </asp:Table>
    </form>
</body>
</html>

And now for the magic. We’ll use these elements to make a service request to VEWS to submit the data in the text box and geocode the location. I’ll take the entire response and break up each part so you can see most of what gets returned in the response. Make sure you add a reference to the GeocodeService we added above.

Geocoding.aspx.cs

using System;
using System.Collections;
using System.Configuration;
using System.Data;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using GeocodeService;

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

    }
    protected void Button1_Click(object sender, EventArgs e)
    {
        //Get user IP for token
        string token = Authentication.Authenticate(Page.Request.UserHostAddress);
        //Instantiate geocode request
        GeocodeRequest myGeocodeRequest = new GeocodeRequest();
        //Instantiate credentials for geocode request
        myGeocodeRequest.Credentials = new GeocodeService.Credentials();
        //Add token to geocode request
        myGeocodeRequest.Credentials.Token = token;
        //Using GeocodeRequest.Query so we don’t need a structured request – just single box
        myGeocodeRequest.Query = TextBox1.Text;
        //Instantiate confidence filter – I just want one filter
        ConfidenceFilter[] myConfidenceFilter = new ConfidenceFilter[1];
        //Instantiate filter
        myConfidenceFilter[0] = new ConfidenceFilter();
        //!Had to specify Geocode Service because Confidence is defined also in Imagery Service!
        //Setting confidence to low so I get maximum results
        myConfidenceFilter[0].MinimumConfidence = GeocodeService.Confidence.Low;
        //Instantiate Geocode options
        GeocodeOptions myGeocodeOptions = new GeocodeOptions();
        //I only want at most 5 results
        myGeocodeOptions.Count = 5;
        //Pass in my confidence filter
        myGeocodeOptions.Filters = myConfidenceFilter;
        //Pass in my geocode options
        myGeocodeRequest.Options = myGeocodeOptions;
        //Instatiate geocode service client for connection to VEWS
        GeocodeServiceClient myGeocodeServiceClient = new GeocodeServiceClient();
        //Physically making VEWS request!
        GeocodeResponse myGeocodeResponse = myGeocodeServiceClient.Geocode(myGeocodeRequest);
        //Loop through results to pull out response and put into a table
        //I’ll only get 1 response, but it could have multiple results
        for(int i = 0; i < myGeocodeResponse.Results.Length; i++)
        {
            TableRow tRow = new TableRow();
            Table1.Rows.Add(tRow);
            TableCell tCell0 = new TableCell();
            tCell0.VerticalAlign = VerticalAlign.Top;
            tRow.Cells.Add(tCell0);
            tCell0.Text = "Formatted Address = ";
            tCell0.Text += myGeocodeResponse.Results[i].Address.FormattedAddress;
            tCell0.Text += "<br> Address Line= ";
            tCell0.Text += myGeocodeResponse.Results[i].Address.AddressLine;
            tCell0.Text += "<br> Locality = ";
            tCell0.Text += myGeocodeResponse.Results[i].Address.Locality;
            tCell0.Text += "<br> Admin District = ";
            tCell0.Text += myGeocodeResponse.Results[i].Address.AdminDistrict;
            tCell0.Text += "<br> Postal Code = ";
            tCell0.Text += myGeocodeResponse.Results[i].Address.PostalCode;
            tCell0.Text += "<br> Country = ";
            tCell0.Text += myGeocodeResponse.Results[i].Address.CountryRegion;
            tCell0.Text += "<br> District = ";
            tCell0.Text += myGeocodeResponse.Results[i].Address.District;
            tCell0.Text += "<br> Postal Town = ";
            tCell0.Text += myGeocodeResponse.Results[i].Address.PostalTown;
            tCell0.Text += "<br> Confidence = ";
            tCell0.Text += myGeocodeResponse.Results[i].Confidence;
            tCell0.Text += "<br> Display Name = ";
            tCell0.Text += myGeocodeResponse.Results[i].DisplayName;
            tCell0.Text += "<br> Entity Type = ";
            tCell0.Text += myGeocodeResponse.Results[i].EntityType;
            tCell0.Text += "<br> Altitude= ";
            tCell0.Text += myGeocodeResponse.Results[i].Locations[i].Altitude;
            /* If there are multiple response types, the latitude, longitude and
             * Calculation methods will have more than one result in their array. This
             * is where you’ll want to differentiate them to use either interpolated,
             * rooftop or parcel centroids depending on how accurate you want them. */
            for (int j = 0; j < myGeocodeResponse.Results[i].Locations.Length; j++)
            {
                tCell0.Text += "<br> Latitude = ";
                tCell0.Text += myGeocodeResponse.Results[i].Locations[j].Latitude;
                tCell0.Text += "; Longitude = ";
                tCell0.Text += myGeocodeResponse.Results[i].Locations[j].Longitude;
                tCell0.Text += "; (";
                tCell0.Text += myGeocodeResponse.Results[i].Locations[j].CalculationMethod;
                tCell0.Text += ")";
            }
        }
    }
}

The end results is a nice little parsing of some address information returned from VEWS:

image

There isn’t any difference in the geocoding results you get back between VEWS and the Virtual Earth AJAX control. Both now have rooftop and parcel centroid geocoding points totaling 85 million in the United States. Now, if I can just figure out what’s up with the space between my text box and my table….[FIXED – tCell0.VerticalAlign = VerticalAlign.Top; ]

CP

Comments (11)

  1. Amy R says:

    We had to modify this code to use GeocodeService instead of GeocodeServiceClient to instatiate the service connection to VEWS. Not sure why this would be the case? Using VS 2005 and the common service staging url

  2. Chris Pendleton says:

    Amy R – this is explained in the SDK: http://msdn.microsoft.com/en-us/library/cc980833.aspx. Essentially, you’ll need a proxy for WCF with VS 2005.

    CP

  3. chuck_richardson says:

    Hi CP,

    Thanks for publishing this sample code. I have having problems when invoking myGeocodeServiceClient.Geocode(myGeocodeRequest). Even though I was able to successfully obtain a client token, the VirtuL Earth service is returning an expection:

    Credentials are either invalid or unspecified

    Any idea why this might be happening?

    Thanks,

    Chuck

  4. Chris Pendleton says:

    Chuck – Did this ever get resolved? We had issues with Staging for a week after launch that have been resolved. Let me know.

    CP

  5. anujkiran says:

    Hi Chris,

    I am facing the same problem as Chuck. Can you please let me know if it got resolved(and how)?

    Thanks in advance.

  6. Chris Pendleton says:

    Anujkiran,

    Login to the Customer Service Site and validate your credentials.

    CP

  7. bhosalevinayak@hotmail.com says:

    Hi Chris,

    I am creating a Web Cloud Service having one Web Role.

    I have added the necessary references for Authentication and for Geocode Service but i get an exception while creating instance saying:

    "An error occurred creating the configuration section handler for system.serviceModel/bindings: That assembly does not allow partially trusted callers. (C:Usersvinayak_bhosaleDocumentsVisual Studio 2008ProjectstestVEtestVEtestVEbinDebugtestVE.csxrolesWebRoleweb.config line 137)"

    When I create a standard web application it works. Is this problem associated with Cloud Service. Please tell me how to solve this problem.

    Thanks in Advance.

  8. Chris Pendleton says:

    bhosalevinayak – this is an Azure issue – I did the same thing. You have to use the config file that comes with the Azure solution. If you overwrite the config you delete all of the service handlers. You can add to the config file, but you can’t copy/paste your project. Check out this post: http://blogs.msdn.com/virtualearth/archive/2009/02/18/microsoft-virtual-earth-on-windows-azure.aspx

    CP

  9. bhosalevinayak@hotmail.com says:

    Hi Chris,

    Thanks for your reply.

    Can you please post the code you created for your blog named "Virtual Earth on Windows Azure" or may be upload them on your sky drive.

  10. rosieBCPD1 says:

    I get this error inconsistently when using GeocodeResponse: "Credentials are either invalid or unspecified".  I have gone to the Customer Service Site and validated my credentials, but I still get this error.

    Rosie

  11. bhosalevinayak@hotmail.com says:

    Hi

    i am trying to implement reverse geocoding and following is the code listing for the same::

    private String ReverseGeocodeAddress(LatLong addr, String token)

           {

               try

               {

                   GeocodeService.ReverseGeocodeRequest revGeocodeRequest = new VETest.GeocodeService.ReverseGeocodeRequest();

                                   revGeocodeRequest.Credentials = new GeocodeService.Credentials();

                   revGeocodeRequest.Credentials.Token = token;

                   Location loc = new Location();

                   loc.Latitude = addr.Latitude;

                   loc.Longitude = addr.Longitude;

                   revGeocodeRequest.Location = loc;

                                   GeocodeService.GeocodeServiceClient geocodeService = new GeocodeService.GeocodeServiceClient("BasicHttpBinding_IGeocodeService");

    geocodeService.ClientCredentials.UserName.UserName = ConfigurationManager.AppSettings["VEUsername"];

                   geocodeService.ClientCredentials.UserName.Password = ConfigurationManager.AppSettings["VEPassword"];

                   GeocodeService.GeocodeResponse geocodeResponse = geocodeService.ReverseGeocode(revGeocodeRequest);

                   return geocodeResponse.Results[0].DisplayName.ToString();

               }

               catch (System.Exception ex)

               {

                   Response.Redirect("ServiceError.htm");

               }

               return string.Empty;

           }

       }

    the code works fine but the geocodeResponse object returns empty result.

    Exception:: return geocodeResponse.Results[0].DisplayName.ToString(); ::Array out of index.

    Can Nybody help me out!!!

    Thanks in Advance.