2004-08-12
| Rate This Article: | Add This Article To: |
Microsoft's MapPoint Web Service is a hosted, programmable Web service that allows you to integrate high quality maps, driving directions, distance calculations, proximity searches, and other location intelligence into your applications, business processes, and Web sites. The Web service is client-agnostic, meaning that you can access it from any system that supports communication with Web services.
In this article, I'll show you how to create a very simple store locator on an ASP.NET Web page. The MapPoint SDK contains a store locator sample, but it is a very complex piece of code that's difficult to understand. The program we'll develop in this tutorial will be much less complex and easier to understand.
This article assumes that you're familiar with creating ASP.NET Web pages and that you've downloaded and installed the MapPoint SDK for .NET. In addition, you must have created a MapPoint evaluation account or have a licensed account with Microsoft. For information about the MapPoint Web Service, see the MapPoint Web Service home page, where you can learn about the product, download or browse the MapPoint SDK, and sign up for an evaluation account.
A Quick Overview
There are four parts to creating a store locator Web page:
- Uploading location data to the MapPoint service
- Creating the user interface
- Interacting with MapPoint
- Displaying results
Of these, the only one that isn't well covered by an SDK sample is the first.
Uploading Location Data
MapPoint contains street address information, but it doesn't contain information about the businesses that are at a particular address. To create a store locator, you need to tell MapPoint the locations of all your stores.
The first step in uploading location data is to create a delimited text file or XML data file. It should contain the names and addresses of your store locations, plus any other information to include for each location, such as its phone number. Once you create this file, you log into your MapPoint Web Service Customer Services site and create a new data source. The Web site prompts you for the data source name, the entity type, and the location of the file to upload. When you press the Create button on this Web page, your browser uploads the file to the MapPoint site, which imports and codes the data.
The format of your uploaded data file is described in the SDK documentation under Formatting MapPoint Web Service Data Source Files and in the description of the CustomerDataService.UploadData method. For a delimited file, the first line must contain the field names, and each following line describes one entity. Each line must have a minimum of three fields: EntityID, Latitude, and Longitude. Other fields are optional. You can include custom fields as long as their names don't conflict with the standard field names.
A sample comma-delimited data file is shown here:
EntityID,AddressLine,PrimaryCity,Subdivision,PostalCode, CountryRegion,Phone,Latitude,Longitude 1,2302 Mayfield Dr.,Round Rock,TX,78681,United States,512-5555555,, 2,103 Louis Henna Blvd,Round Rock,TX,78664,United States,512-5551234,, 3,1410 Cypress Creek Rd,Cedar Park,TX,78613,United States,512-5550987,, 4,2401 N I-35,Round Rock,TX,78664,United States,512-5559630,, 5,2701 S I-35,Round Rock,TX,78664,United States,512-5557412,, 6,7409 FM-620,Austin,TX,78726,United States,512-5558520,, 7,1105 N I-35,Round Rock,TX,78664,United States,512-5552468,, 8,1103 N Bell Blvd,Cedar Park,TX,78613,United States,512-5557391,,
The EntityID field must be unique for each entity.
The first line would ordinarily be on one line. It's broken up for online formatting reasons.
You probably noticed that the Latitude and Longitude fields are blank in this example. If you're sure you have good addresses for the locations, you can leave the locations out; MapPoint will geocode the data for you—automatically adding the Latitude and Longitude fields. If you do include the location information, the numbers should be valid floating-point values. For example, the location of the last item in the above list is latitude: 30.52204244, longitude: -97.83078024.
Creating the User Interface
The first thing you should do when you create a new project that's going to use MapPoint is add a Web reference for the MapPoint Web Service. To do that, right-click on the "References" node of Solution Explorer and select "Add Web Reference." When prompted for the URL in the Add Web Reference dialog box, enter the URL to the MapPoint staging server: http://staging.mappoint.net/standard-30/mappoint.wsdl, and then click on the Go button. Visual Studio will download the WSDL document. Before you click the "Add Reference" button, change the Web reference name field to "MapPointService".
For the sake of brevity, I made the user interface for this sample application very simple. It contains a text box in which the user can enter an address, a Find button, and an empty image control that will display the returned map image. The idea is that the user will enter his address and click Find. The Web page queries MapPoint to return a map that shows all of the store locations within a five-mile radius of the point entered.
Again for brevity, all of the processing in this sample takes place in the Find button's Click event handler.
Interacting with MapPoint
We make three calls to the MapPoint Web Service before we can display a map. We have to:
- Get a MapPoint Location object that represents the address that the user input;
- Get a list of the store locations that are within five miles of that location;
- Get a map that shows the input address and all of the store locations that are within range.
Preliminaries
Almost all of the code in this article was cribbed from one or another of the sample programs included in the MapPoint SDK. I borrowed heavily from their FindAddress and StoreLocator samples. The idea behind this is to extract the essential code from the user interface fluff, so you can get a very clear idea of how to call the MapPoint Web Service.
One block I copied verbatim is the InitializeMPNetConnection method that's located in the Global.asax file. This code initializes a number of objects that are used to communicate with the Web service. InitializeMPNetConnection is called by the
Global object constructor, when the Web page is loaded.
The code also uses the <appSettings> section of the application's Web.config file to locate the user ID and password for the MapPoint Web Service. To use this code in your application, you'll need to add an <appSettings> element to your Web.config file. Here's a sample:
<appSettings> <add key="MPUser" value="UserID" /> <add key="MPPass" value="Password" /> </appSettings>
You would need to replace the values with the user ID and password that you received from Microsoft when you signed up for your evaluation account.
One final note. The code listings shown in the rest of this article make use of two global variables, shown here:
private MapPointService.FindResults foundResults; private MapPointService.FindResults foundStores;
These are intermediate result variables that are created by calls to the MapPoint Web Service.
Finding an Address
The first task is to turn the input address into a form that we can use to represent a location in MapPoint. In the sample code, that's handled by the FindStartingAddress method, shown here.
private bool FindStartingAddress()
{
// Setup the find options
MapPointService.FindRange myFindRange = new MapPointService.FindRange();
// Let's limit the number of possible returned results to 100
// Your application may be different, or you might want to
// store this as an application configuration option
myFindRange.Count = 100;
// For this sample, we always display only the first n results
// In your actual application you might want to allow the user to
// display the next set of results.
// If so, change the start index here appropriately.
myFindRange.StartIndex = 0;
// Now we need to set the find options
MapPointService.FindOptions myFindOptions =
new MapPointService.FindOptions();
// The threshold score is a cutoff after which no results
// will be returned. For this sample, we will just return
// all potential results.
myFindOptions.ThresholdScore = 0;
myFindOptions.Range = myFindRange;
// Now we need to set the context based on the country
myFindOptions.SearchContext = 244; // United States
// Setup the find source, default to North America data
string myDataSourceName = "MapPoint.NA";
// set the datasource and language appropriately in the header.
global.FindService.UserInfoFindHeaderValue.Culture.Name =
System.Threading.Thread.CurrentThread.CurrentCulture.Name;
// Set up the find specification
MapPointService.FindAddressSpecification findSpec =
new MapPointService.FindAddressSpecification();
// Create and populate Address object for FindAddress() operation.
MapPointService.Address myAddress =
new MapPointService.Address();
myAddress.FormattedAddress = txtAddress.Text;
// search for this address
findSpec.InputAddress = myAddress;
findSpec.DataSourceName = myDataSourceName;
findSpec.Options = myFindOptions;
// Call the find service and retrieve the results
foundResults = global.FindService.FindAddress(findSpec);
// If we have any results
if( null != foundResults && foundResults.Results.Length > 0 )
{
return true;
}
return false;
}
The FindAddress method is pretty versatile. It can locate based on full or partial addresses, and returns an array of candidate matches if it can't find an exact match for the input address. In this sample I used a simple formatted address as the input, but you can also set the individual address fields if you want.
The important parts to get right here are two "magic" values that tell MapPoint what address data to search. To search for addresses in the United States, set the FindOptions.SearchContext value to 244 and the DataSourceName to "MapPoint.NA" (North America). See the SDK documentation for the proper settings for other
locales.
Once you've populated the FindAddressSpecification, you can call the MapPoint Web Service to obtain the location for the input address. If MapPoint finds an exact match, there will be only one result in the returned FindResults. Otherwise there will be multiple results, and you'll either have to pick one or let the user pick one.
Finding Nearby Stores
Given a location, we're now ready to ask MapPoint to return a list of the store locations that are within a given radius. Here's where we make use of the location data that we uploaded at the beginning of this article. If we pass the location that corresponds to the user's address and a radius to MapPoint, it returns to us a list of stores that are in that area. The code to make the request is pretty simple:
private bool FindNearbyLocations()
{
// FindStartingAddress returned candidate locations in foundResults.
// This demo will just pick the first one.
MapPointService.FindResult selectedResult =
foundResults.Results[0];
// set the default distance unit to miles
global.FindService.UserInfoFindHeaderValue.DefaultDistanceUnit =
MapPointService.DistanceUnit.Mile;
// then get the location of the search
MapPointService.LatLong center =
selectedResult.FoundLocation.LatLong;
// next, get the radius of the search
double searchRadius = 5;
// Setup the find
// now setup the find. We need to allocate a find range
MapPointService.FindOptions findNearbyOptions =
new MapPointService.FindOptions();
findNearbyOptions.Range = new MapPointService.FindRange();
// Let's limit the number of possible returned results to 20
// Your application may be different, or you might want to
// store this as an application configuration option
findNearbyOptions.Range.Count = 20;
// For this sample, we always display only the first n results
// In your actual application you may allow the user to display
// the next set of results. If so, change the start index here
// appropriately.
findNearbyOptions.Range.StartIndex = 0;
// now setup the MapPoint Web Service datasource.
// We have our own datasource we want to search
// (The source containing our only our store locations).
// You will want to use your own source here.
MapPointService.DataSource datasource =
new MapPointService.DataSource();
// enter the name of your data source
datasource.Name = "Enter Source Name";
// now we need an entity-type name to tell
// MapPoint Web Service what to search on
string entityTypeName;
// set the name to our coffee shop name, which will retrieve
// all shops. What you search on is totally dependant on the
// data you stored with your datasource. You might want to
// filter on the type of store if you had several types.
entityTypeName = "store";
// For our purposes, that's all we need. We have our
// own datasource, so we don't need to filter on any property
// sets or use any of the other options. You may want to
// filter on certain properties, or use some of the other find
// nearby options. See MapPoint Web Service SDK documentation
// for more details.
// Set up the find nearby specification
MapPointService.FindNearbySpecification findNearbySpec =
new MapPointService.FindNearbySpecification();
findNearbySpec.DataSourceName = datasource.Name;
findNearbySpec.LatLong = center;
findNearbySpec.Distance = searchRadius;
findNearbySpec.Filter = new MapPointService.FindFilter();
findNearbySpec.Filter.EntityTypeName = entityTypeName;
findNearbySpec.Filter.PropertyNames = new string[] {"Phone"};
// Now we execute the query.
// Note that like all calls we make to MapPoint Web Service,
// this call is sychronous, even though it can take a long
// time to execute. There are asynch flavors of the MapPoint
// Web Service APIs, but in a Web page, it does not make
// sense to use them.
foundStores = global.FindService.FindNearby(findNearbySpec);
if (foundStores == null || foundStores.Results.Length == 0)
{
return false;
}
return true;
}
Most of that code is just setting up default values for the call. However, a few things are very important. First, you'll want to set the searchRadius to the proper value. In a real Web application, you'd prompt the user for the radius. Here I just default it to five miles.
Since MapPoint will be searching your datasource, you need to tell it the name of the datasource and the entity type for which it should be searching. In the FindNearbyStores method shown above, those values are set in the datasource.Name and entityType variables.
Once everything's set up, a call to FindService.FindNearby will return a collection of entities that are within the given radius.
Getting the Map
Setting all the options for the map that we want returned is the most difficult part of the program. We want to show the user's address with a green flag, and show all of the store locations with little "drive through" icons.
In order to get the map, we have to tell MapPoint for what area to return a map, and what icons we want on the map. The only difficulty is in figuring out which properties to set on which objects. Many of the objects have constituent objects several levels deep, and it's easy to get confused about what goes where. I think I've boiled it down to be as simple as possible and still show all of the required features. Here's the code:
private bool GetLocationMap()
{
MapPointService.MapSpecification mapSpec =
new MapPointService.MapSpecification();
MapPointService.MapOptions mapOptions =
new MapPointService.MapOptions();
// have MapPoint return a URL to the map image.
mapOptions.ReturnType = MapPointService.MapReturnType.ReturnUrl;
mapOptions.Format = new MapPointService.ImageFormat();
mapOptions.Format.Width = 640;
mapOptions.Format.Height = 480;
mapOptions.Style = MapPointService.MapStyle.Road;
// starting location
MapPointService.FindResult selectedResult =
foundResults.Results[0];
MapPointService.LatLong center =
selectedResult.FoundLocation.LatLong;
mapSpec.DataSourceName =
selectedResult.FoundLocation.DataSourceName;
mapSpec.Options = mapOptions;
// Add pushpins for all of the found results
// and the selected location
MapPointService.Pushpin[] pins =
new MapPointService.Pushpin[foundStores.Results.Length + 1];
pins[0] = new MapPointService.Pushpin();
pins[0].IconDataSource = "MapPoint.Icons";
pins[0].LatLong = center;
pins[0].IconName = "31"; // green flag icon
for (int i = 0; i < foundStores.Results.Length; i++)
{
pins[i+1] = new MapPointService.Pushpin();
pins[i+1].IconDataSource = "MapPoint.Icons";
pins[i+1].LatLong = foundStores.Results[i].FoundLocation.LatLong;
pins[i+1].IconName = "DriveThruIcon";
}
// Create the map view to include all of the found
// results and the selected location.
MapPointService.ViewByBoundingLocations[] views =
new MapPointService.ViewByBoundingLocations[1];
views[0] = new MapPointService.ViewByBoundingLocations();
MapPointService.Location[] locations =
new MapPointService.Location[foundStores.Results.Length + 1];
locations[0] = new MapPointService.Location();
locations[0].LatLong = center;
for (int iRslt = 0; iRslt < foundStores.Results.Length; iRslt++)
{
locations[iRslt+1] = new MapPointService.Location();
locations[iRslt+1] = foundStores.Results[iRslt].FoundLocation;
}
views[0].Locations = locations;
mapSpec.Views = views;
mapSpec.Route = null;
mapSpec.Pushpins = pins;
// go get the map
MapPointService.MapImage mapImage =
global.RenderService.GetMap(mapSpec)[0];
if (mapImage == null)
return false;
// Set the image URL to the returned map
imgMap.ImageUrl = mapImage.Url;
return true;
}
This code builds the constituent objects that are contained in the MapSpecification object, and then sets those values into the mapSpec before calling the RenderService.GetMap Web service method. GetMap requires that you set at least one of Views or Route. If both properties are null, the Web service throws an exception. I left Route null for this sample because I didn't want to go into the driving directions stuff.
Displaying Results
Displaying results in this program is entirely too simple: just set the ImageUrl property on the image component that we dropped on the form to the Url returned in the MapImage object from the Web Service. Nothing to it. Here's a picture of the Web page showing the results:
Obviously, there's much more to MapPoint than the little bit that I showed in this brief article. My purpose was to show you the basic steps you need to follow to render a map of a given area with points of interest highlighted. Given knowledge of the bare minimum, you should now be ready to begin adding features or modifying the SDK StoreLocator sample with a little more confidence.
|
![]() |
|


