Virtual Earth @ Tech Ed ’07

Thanks to everyone who attended my Virtual Earth / Live.com Search speaking session and visited me at the Live.com booth @ Tech Ed this year. During my discussion I had promised I would post my app code for converting SOAP XML to JavaScript, so you’ll find step-by-step instructions below. I had originally written this on the B2B blog and have since updated it for Version 5 of the API. Specifically, this application will show you how to use MapPoint Web Service’s FindNearRoute() method to find the nearest locations with .1 miles of a route then display the results on a Virtual Earth map. Enjoy!

An application hitting MapPoint Web Service (MWS) for algorithmic searching (in order to be presented atop Virtual Earth (VE) requires some code-behind programming. The following example will illustrate how to do this in C#.

The application consists of several parts, some of which can be consolidated for ease of use but I broke them up as this would be the best architecture for application develpment. Also, I should not that I don’t do much error handling and you definately want to add that! This is quick and dirty – it is a blog after all.

So, once you have your WSDL setup you’ll want to have a few files as a part of your project.

Default.aspx – This is where you’ll display your results on the Virtual Earth map.MWSServices.cs – Class file for storing methods to connect to MWS.MWSVE.js – A JavaScript file for wrapping and parsing MWS requests.FindNearRoute.aspx – The front end for our code behind file – we won’t actually use this, but we need it.FindNearRoute.aspx.cs – Class file storing most of the MWS code.

I’ve documented much of the project inline for better use. The flow of the application goes like this –

(1) Users are dropped onto a web page with 2 input boxes and a VE map.

(2) Users enter two postal codes (origin and destination). That information is sent to the MWSVE.js file.

(3) The MWSVE.js file will create a URL for posting to FindNearRoute.aspx file.

(4) FindNearRoute.aspx.cs will parse the URL and process the postal codes to MWS (via the MWSServices.cs file). The file will geocode the two points, calculate the route, then calculate the locations along the route. Once the calculation is complete, I create a StringBuilder to post to the MWSVE.js file.

(5) The MWSVE.js file will parse the StringBuilder and post it (eval) to default.aspx and viola we have a map with a route and locations along the route.

//////////////////////////////////////////FindNearRoute.aspx – note that I deleted all of the default code but left the header.////////////////////////////////////////<%@ Page Language=”C#” AutoEventWireup=”true” CodeFile=”FindNearRoute.aspx.cs” Inherits=”FindNearRoute” %>

/*We don’t have any code on the .aspx portion of this file because we aren’t using it. * We want to load this function as soon as the method requests it from js methods */ protected void Page_Load(object sender, EventArgs e) { //Grab the origin and destination from the URL parameters string origin = Request[“opc”]; string destination = Request[“dpc”];

/* We’ll use the coordinates from the postal codes to create a route. * We need this in order to calculate a route using MapPoint Web Service. We * do this because the algorithm for finding points along a route is included * in MWS. The coordinates need to be included in the LatLong[] in order to * be passed to CalculateSimpleRoute.*/ LatLong[] routePoints = new LatLong[2]; routePoints[0] = new LatLong(); routePoints[0].Latitude = dblGeocodedOrigin[0]; routePoints[0].Longitude = dblGeocodedOrigin[1]; routePoints[1] = new LatLong(); routePoints[1].Latitude = dblGeocodedDestination[0]; routePoints[1].Longitude = dblGeocodedDestination[1];

/* Request the route from MWS. We could also use CalculateRoute(), but I have a simple * route and don’t need the segment information. I’ll pass in the coordinates, * the geographic regions (MapPoint.NA is North America), and the route calculation * preference which is either Quickest or Shortest. You’ll want to make sure * this aligns with the segment preference you set in your VE route, as well*/ Route myRoute = routeService.CalculateSimpleRoute(routePoints, “MapPoint.NA”, SegmentPreference.Quickest);

/* We need to find the locations along the route. We do that by creating a routespecifiction. * This is not a geographic request since we want to find YOUR CUSTOM locations which * you’ve uploaded into MWS. The FourthCoffee data source is available to everyone * with an MWS account and can be used for demo purposes.*/ FindNearRouteSpecification findNearRouteSpec = new FindNearRouteSpecification(); findNearRouteSpec.DataSourceName = “MapPoint.FourthCoffeeSample”; /* Distance from the route in a cylindrical shape following the route. I’ve * set this in mile in the Constants.cs file. It can be changed to kilos as well. */ findNearRouteSpec.Distance = .1; /* Filter is required to specify your EntityTypeName. You’re allowed many EntityTypeNames * per data source. Think of a data source as a data base and your EntityTypeName * as your tables.*/ findNearRouteSpec.Filter = new FindFilter(); findNearRouteSpec.Filter.EntityTypeName = “FourthCoffeeShops”; /* FindOptions are just that – optional. MWS has defaults for all of these, but you * can extend the functionality by specifying values. * Options.Range.Count is the most amount of records you want returned in the response. * Route is the route object created above my MWS */ findNearRouteSpec.Options = new FindOptions(); findNearRouteSpec.Options.Range = new FindRange(); findNearRouteSpec.Options.Range.Count = 500; findNearRouteSpec.Route = myRoute;

//Now send your routespecification to MWS to get the locations along a route. FindResults myFindNearRouteResults = findService.FindNearRoute(findNearRouteSpec);

/* Take the results for locations near a route and display them on a VE map. * There are many ways to do this, but I prefer a StringBuilder. First, I want to delete * any pushpins that may be on the map from a previous query. */ //StringBuilder sbPins = new StringBuilder(); StringBuilder sbPointsAlongRoute = new StringBuilder(); //Delete any previous layers sbPointsAlongRoute.Append(“map.DeleteAllShapeLayers();”); /* Now, we just build the VEShape object for Virtual Earth and loop through each record * in the MWS response. */ sbPointsAlongRoute.Append(“var myPinArray = new Array(“);

//Finally, write the javascript into the page. Response.Write(sbPointsAlongRoute); }

/* The following method will request lat/long coordinates from * MWS based on a postal code input from the user.*/ public static double[] GeocodePostalCodes(string postalcode) { //We use Find service to get postal codes from MWS FindServiceSoap findService = MWSServices.findservice(); //Setup a FindSpecification to create the MWS request FindSpecification findSpec = new FindSpecification(); //This is a geographic search, so we need to specify North America findSpec.DataSourceName = “MapPoint.NA”; //Input postal code findSpec.InputPlace = postalcode; findSpec.Options = new FindOptions(); findSpec.Options.Range = new FindRange(); /* I only need a single lat/long coordinate, so I set Options.Range.Count to 1. * This will help with performance by limited the response packet size. */ findSpec.Options.Range.Count = 1; /* I also ONLY need the lat/long coordinate, so I can specify that here. This * also help performance be further limiting the response packet size. */ findSpec.Options.ResultMask = FindResultMask.LatLongFlag; // SearchContext specifies the country. 244 is the US. findSpec.Options.SearchContext = 244; //Threshold score is an accuracy rating system findSpec.Options.ThresholdScore = .85;