Becky Bertram's Blog
Perspectives from a SharePoint developer




This blog has moved. You can find the new blog on Savvy Technical Solution's Web site, at www.savtechsol.com. You will be redirected to the following page in 10 seconds:

Becky Bertram's Blog > Posts > Using Yahoo! Maps to Create a Locator Application
September 17
Using Yahoo! Maps to Create a Locator Application

​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>
<!-- ws03.search.re2.yahoo.com uncompressed/chunked Wed Sep 19 17:11:35 PDT 2007 -->


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&latiude=38.895325&zoom=3&image_height=300&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.
To download the sample application, click here.

Comments

There are no comments for this post.

Add Comment

Items on this list require content approval. Your submission will not appear in public views until approved by someone with proper rights. More information on content approval.

Comment Title *


Body *


Your Name


Your E-mail Address


Your Website

Type the Web address: (Click here to test)  

Type the description: 

Are you spamming my blog? *


This field is here in an attempt to stop spammers from entering comments. Enter a number, any number

Attachments