It’s very common for a company to want to create a section of their web site where users can type in their address and figure out which location of a business is closest to them. Luckily, mapping technology has had lots of big backers, such as Microsoft, Google, and Yahoo!, so there are plenty of choices out there, (not to mention one of the most familiar players in the game, MapQuest.) For companies that foresee having thousands of hits a day to their application, you can purchase packages from these companies that support a large load. However, for small or medium sized businesses, it doesn’t seem practical to spend tens of thousands of dollars on such a service. I was happy to discover that many of these mapping applications offer APIs that are free for use, as long as the hits remain below a certain level (which is actually quite generous.)

One of the most widely-used forms of AJAX today is on mapping web sites. Although I am a big fan of dragging and zooming a map with the click of my mouse using AJAX, my goal was to create an application in the quickest amount of time. Not being a Javascript guru, I decided to go with Yahoo! Maps, since they had one of the simplest API’s to use. Although they have a rich API for Flash and Javascript developers, their “Simple API” (see? I told you it was simple) relies on passing structured requests to the server, much like a simple web service.

To learn about the Mapping API’s, the first step is to go to Yahoo’s Developer center at http://developer.yahoo.com/maps/. You can specifically learn about the Simple API at http://developer.yahoo.com/maps/simple/index.html.

The locator application has to work with Yahoo! Maps in five capacities:

  1. Displaying a map of the location the user typed in (i.e. starting point).
  2. Having a URL to Yahoo! Maps, so when the user clicks on the image of their location, they’re taken to the Yahoo! Maps web site at that location.
  3. Displaying a map for each location in the search results.
  4. Having a URL to Yahoo! Maps for location.
  5. Most importantly, calculating the distance between the starting point and each location.

Although the free API has a rather high threshold for hits, it still makes sense to not have to query Yahoo every time a location is pulled up in the results. This both reduces the number of hits to the service, but also increased performance. It makes more sense to query the location when it is saved by the back-end user, and then stored in a database. Based on the criteria above, we need to get three pieces of information for each location: the map image itself, a URL to the Yahoo! Maps site, and location coordinates (longitude and latitude) for each location.

The first step is to find the location coordinates for the location. You can do this by passing in Address, City, State, and Zip Code information to Yahoo. Yahoo will automatically calculate the closest distance, based on the information you pass it. It will not only return to you the longitude and latitude coordinates of the location, it will also return the address that it searched, as well as any error messages. For instance, if a user enters a city and a state, and Yahoo cannot find that city in the state, the address it returns shows that it just returned the center point for the state alone, along with an error message that it couldn’t find the city.

The Yahoo! Maps simple API uses a simple POST to get the information needed. I created a namespace called Yahoo and created a class called Maps inside that namespace. (All my code is written in VB.NET.) My code is based off a sample from the Yahoo! Maps developer site listed above.

Public Function GetResponse(ByVal address As Uri, ByVal data As String) As String

Dim request As HttpWebRequest = DirectCast(WebRequest.Create(address), HttpWebRequest)
' Set type to POST
request.Method = "POST"
request.ContentType = "application/x-www-form-urlencoded"
Dim appId As String = ConfigurationManager.AppSettings("YahooAppId")

data = "appid=" & ConfigurationManager.AppSettings("YahooAppId") & data

' Create a byte array of the data we want to send
Dim byteData() As Byte = UTF8Encoding.UTF8.GetBytes(data)

' Set the content length in the request headers
request.ContentLength = byteData.Length

Dim postStream As Stream = Nothing
Dim response As HttpWebResponse = Nothing

Try
  postStream = request.GetRequestStream()
  postStream.Write(byteData, 0, byteData.Length)
Catch
 Throw
Finally
  If Not postStream Is Nothing Then postStream.Close()
End Try

Try
' Get response
  response = DirectCast(request.GetResponse(), HttpWebResponse)

  ' Get the response stream into a reader
  Dim reader As StreamReader = New StreamReader(response.GetResponseStream())

  Dim xml As String = reader.ReadToEnd()
  Return xml
Finally
  If Not response Is Nothing Then response.Close()
End Try

End Function

As you can see, the function takes two parameters. The first is the URL the request is being posted to. The process of using an address to retrieve latitude and longitude coordinates is called Geocoding. I have stored the request URL in the web config. The URL I am using is http://local.yahooapis.com/MapsService/V1/geocode. The second value I’m passing in is a long string of data. The structure of the data can be found on the Yahoo! Maps developer site.

In order to query the Yahoo! Maps API, you need to have a special ID assigned to you. I have stored the YahooAppId in the web.config as well. After I have created a new HttpRequest object, I append the Yahoo App Id to the data that will be passed to Yahoo. Once that’s done, I look for a response from the server. The response will contain a stream of XML.

So, let’s try it out. Let’s pass in an address and see what response we get back. Here’s my function for getting XML from an address:

Public Function GetAddressXml(ByVal street As String, ByVal city As String, ByVal state As String, ByVal zip As String) As String

Dim data As StringBuilder = New StringBuilder()
data.Append("&street=" + HttpUtility.UrlEncode(street))
data.Append("&city=" + HttpUtility.UrlEncode(city))
data.Append("&state=" + HttpUtility.UrlDecode(state))
data.Append("&zip=" + HttpUtility.UrlDecode(zip))

Dim addressXml As String

Try
  addressXml = GetResponse(New Uri(ConfigurationManager.AppSettings("YahooGeoCodeUrl")), data.ToString())
Catch
  Throw
End Try

Return addressXml

End Function 

To call this code, I’ll pass in the values:1600 Pennsylvania Ave NW, Washington, D.C., and 10001. This is the XML I get back:

<?xml version="1.0"?>
<ResultSet xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="urn:yahoo:maps" xsi:schemaLocation="urn:yahoo:maps http://api.local.yahoo.com/MapsService/V1/GeocodeResponse.xsd">
<Result precision="address" warning="The exact location could not be found, here is the closest match: 1300 Pennsylvania Ave Nw, Washington, DC 20004">
<Latitude>38.895325</Latitude>
<Longitude>-77.029816</Longitude>
<Address>1600 PENNSYLVANIA AVE NW</Address>
<City>WASHINGTON</City>
<State>DC</State>
<Zip>20004-3002</Zip>
<Country>US</Country>
</Result>
</ResultSet>

Some things to note about this XML:

  • There’s an attribute on the result set called “precision”. As the name says, it denotes how precise the results are. In this example, Yahoo found the street address passed in. If only a state had been passed in, the precision would be “state” and so on.
  • Note the warning attribute of the Result node.
  • If you are searching an address in North America, the longitude will most likely be a negative number, indicating that it’s west of Greenwich. The latiude will most likely be a postiive number, idicating it’s north of the equator. Latitudes can be a number from 0 to 90° and longitudes can be a number from 0 to 180°. If you were to search for an address beyond those ranges, you would receive an error.

At this point, it’s smart to save the coordinates in the database for that location. The next step is to get the map image from Yahoo based on those coordinates. You can do this by passing in a data string such as this:

longitude=-77.029816&amp;latiude=38.895325&amp;zoom=3&amp;image_height=300&amp;image_width=150.

Please note that there’s a different request URL to send to the GetResponse function. It’s http://local.yahooapis.com/MapsService/V1/mapImage.

This will return the URL of an image at that location. The image will be from a Zoom level of “3” (slightly higher than street level), and will have a width of 150 pixels and a height of 300 pixels. I would store this URL in the database as well, for the reasons mentioned earlier. Something to note: when using the “Broadband” (i.e. flash and javascript enabled) verison of Yahoo! Maps, the “location” coordinates determine where the center of the map is, and the address simply shows a little balloon showing where that location is. If the address is the same as the latitude and logitude, this is one and the same. However, it’s possible to center the map on some point different than the address, so the pop up balloon is not the same as the longitude and latitude coordinates of the center of the map. However, when using the Simple API, the image that’s retrieved for you uses the old-school Yahoo map, which means the star in the middle of the map is determined by the latitude and longitude coordinates, not by the address, as in the newer, broadband version.

The last step is to figure out which URL to send the user to, so that when they click on the image of the location, it will take them to the Yahoo map of the same location. I will freely admit, I could not find any information on Yahoo’s site about how to determine this address. There was significant information about how to make Yahoo AJAX-style functionality work on your own site, but nothing that I could find about how to link to that location on Yahoo maps. So, what I came up with was a URL like this:

http://maps.yahoo.com/flash/index.php#lon=-77.029816&lat=38.895325&mag=3&q1=1300%20Pennsylvania%20Avenue%20Washington%20D.C.%1001

As I just explained, when using the Broadband map, the “lon” and “lat” query string parameters indicate where the map should be centered, and the “q1” parameter is a URL-encoded string made of the concatenated address. This will indicate where the balloon will appear. Futhermore, the addrress of the balloon shows up in the address text boxes on the left side of the page. The “mag” paramters is the same as the “zoom” parameter passed in to get the map; it indicates the zoom of the map.

There’s a bit of a gotcha to keep in mind, and that is that not everyone is using the broadband version of Yahoo! Maps. Yahoo uses a cookie to remember which kind of map you like to use. If you notice, the URL points to the Flash version. Luckily, Yahoo is smart enough to redirect the user to the same page and location in the low-bandwidth version if they are using that version of Yahoo! Maps.