Ajax Hacks: Tips & Tools for Creating Responsive Web Sites

Hack 30. Use Ajax with a Google Maps and Yahoo! Maps Mash-up

Use Google Maps in a web application with Yahoo! Maps and driving directions.

Both Google and Yahoo! provide developers with power tools for manipulating maps within their own web applications. "Get Access to the Google Maps API" [Hack #28] introduced readers to the Google Maps API; Yahoo! in turn provides the Yahoo! Maps API, which includes a specific API for Ajax developers (see http://developer.yahoo.net/maps/ajax/index.html).

Yahoo! Maps is very easy to get started with; just acquire an application ID from the above URL, then begin embedding Yahoo! Maps and controls inside your web pages. You have to include the application ID in the URL that embeds a map inside your web page, which you'll learn how to do in the upcoming hack description.

This hack uses both the Yahoo! Maps and Google Maps APIs. Combining two technologies in a web application is sometimes referred to as a mash-up, an expression that derives partly from the music industry. This way we can view a Google Map on the left side of the screen, perhaps in satellite view, and the same geographical region in a Yahoo! Map on the screen's right side. The mash-up also allows the user to click on a destination on a Google Map and access Yahoo's driving directions.

How It Works

This hack first sets up a Google Map on a web page using the Google Maps API. The application asks the user to click on the map to specify a map coordinate, and optionally, type in an origin address for driving directions. Users can zoom in on different map coordinates and areas prior to clicking a destination point.

The destination is specified in terms of latitude and longitude. A little balloon icon pops up on the Google Map wherever the mouse is clicked. When the user clicks the Yahoo! Map button, a Yahoo! Map appears on the screen's right side, centered on the specified latitude and longitude. The user can then optionally put the Google Map into satellite view, while manipulating the Yahoo! Map with its sophisticated controls.

To obtain driving directions, the user can enter an origin address in the left side text fields and then click the Yahoo! Directions button. The application uses the indicated latitude/longitude coordinates to scrape the driving directions off of a Yahoo! HTTP response. The hack then replaces the Yahoo! Map with the step-by-step driving directions.

Figure 4-2 shows what the application looks like in a web browser before the user clicks the Yahoo! Map button.

Figure 4-2. Choose your destination

The HTML page divides the application into two regions using div tags and CSS styles. The Google Map sits on the left side. Figure 4-3 shows the mash-up after the user clicks on the Google Map to specify a coordinate, then clicks the Yahoo! Map button. The Yahoo! Map is shown on the right side of the screen.

Figure 4-3. Google and Yahoo!, duking it out

Figure 4-4 shows the application when the user has changed to satellite mode in the Google Map and zoomed out a bit in the Yahoo! Map.

Figure 4-4. Changing to satellite mode

If the user requests driving directions, they appear in the right part of the screen, as in Figure 4-5.

Figure 4-5. Marrying Google Maps and Yahoo! directions

Google Maps provides latitude/longitude coordinates for anywhere on earth, but Yahoo!'s driving directions cannot presently provide directions between places that are separated by bodies of water such as oceans or bays. If you click on an island in a Google Map, for example, you will get the latitude/longitude point. However, the directions will be blank because Yahoo! driving directions, as of this writing, do not connect mainland origins with island destinations.

Fall Harvest

A good chunk of the work for the driving-directions mechanism is done by the server component, which harvests the directions from a Yahoo! page. Specifically, the Google request object sends along a Yahoo!-related URL that includes the user's chosen latitude and longitude coordinates. The server component then:

  1. Makes a request to Yahoo! using the URL

  2. Receives all the code for the Yahoo! page

  3. Scrapes the driving directions from the page

  4. Sends just this chunk of data back to the application, which displays the directions in the right frame

This chunk of data in the response is a div element containing a numbered list of driving directions, as in:

  1. Take a right on Main Street and go 1.2 miles.

  2. Go on the highway for another 680 miles....

HTML

Let's look at some of the HTML code for the page that's loaded into the mash-up user's browser. I'll just show the top part of the HTML code that sets this application in motion:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <style type="text/css"> .cont { float: left; height: 500px; width: 500px; } .instructions { font-size: 0.8em; } .label { font-size: 0.9em;} </style> <script src="http://maps.google.com/maps?file=api&v=1&key=ABQIAAAANJd_PEMs2vnU_ f0RhwHhZhSkdb7FxCoFqdzTrRB9tjTtDcnrVRSo66iNyUFvtz5XXXXXXXXXXX" type="text/javascript"></script> <script src="/books/4/254/1/html/2//ajaxhacks/js/http_request.js" type= "text/javascript"></script> <script src="/books/4/254/1/html/2//ajaxhacks/js/hacks4_1_b.js" type= "text/javascript"></script> <script type="text/javascript" src="http://api.maps.yahoo.com/ajaxymap?v=2.0&appid=YRXXXXXXXXXXX"></script> <title>Map Mash-Up</title> </head> <body> <h3>Use Google Maps and Yahoo! Maps</h3> <div > <div ></div> ...

The first script tag imports the Google Maps objects that we can use as official Google hackers. These are JavaScript objects such as GMap that allow the code to add and control maps on a web page. The src attribute of the script tag includes the long, involved URL for importing Google's special code, as well as the developer-specific key [Hack #28]. (The XXXs have been added because we shouldn't publish the exact keys we are using.)

The JavaScript code appears in the file hacks4_1_b.js. script tags import that code, which you'll see in an upcoming section, as well as the code that uses XMLHttpRequest [Hack #3]. Finally, the HTML code imports the Yahoo! Mapsrelated code with another script tag. This code base is necessary for web pages that embed Yahoo! Maps. The URL for this purpose includes an appid parameter specifying your own application ID for Yahoo! Maps:

<script type="text/javascript" src="http://api.maps.yahoo.com/ajaxymap?v=2.0&appid=YRXXXXXXXXXXX"></script>

Google and Yahoo!, Toe to Toe

Here is the JavaScript code in the hacks4_1_b.js file. The createMap( ) function does not need further explanation because we discussed it in the last hack. Let's focus on the code that embeds a Yahoo! Map and provides driving directions:

var map = null; window.onload=function( ){ createMap( ); document.getElementById("submit").onclick=function( ){ getDirections(document.forms[0]._street.value, document.forms[0]._city.value, document.forms[0]._state.value, document.forms[0]._dname.value, document.forms[0]._latitude.value, document.forms[0]._longitude.value); }; document.getElementById("rem_bubbles").onclick=function( ){ clearOverlays( ); }; document.getElementById("yah_maps").onclick=function( ){ createYMap( ); }; }; function createYMap( ){ writeMap(document.forms[0]._latitude.value, document.forms[0]._longitude.value); } function createMap( ){ map = new GMap(document.getElementById("map")); GEvent.addListener(map, 'click', function(overlay, point) { document.forms[0]._longitude.value=point.x; document.forms[0]._latitude.value=point.y; map.addOverlay(new GMarker(point)); }); map.addControl(new GLargeMapControl( )); map.addControl(new GMapTypeControl( )); //center on roughly middle of USA map.centerAndZoom(new GPoint(-97.20703, 40.580584), 14); } function clearOverlays( ){ if(map != null){ map.clearOverlays( ); } } function getDirections(street,city,state, destName,lat,lng){ var _str = encodeURIComponent(street); var _cit = encodeURIComponent(city); var url = "http://www.parkerriver.com/s/dd?"+_str+"&tlt="+ lat+"&tln="+lng+"&csz="+ _cit+"%2C"+state+"&country=us&tname="+destName; httpRequest("GET",url,true,handleResponse); } //event handler for XMLHttpRequest function handleResponse( ){ try{ if(request.readyState == 4){ if(request.status == 200){ var _dirs = request.responseText; var targDiv = document.getElementById("ymap_container"); targDiv.innerHTML=_dirs+ '<p><form><button type=\\"button\\" onclick=\\ "window.print( )\\">Print Directions</button></form></p>'; } else { //request.status is not 200; ommitted for brevity } }//end outer if } catch (err) { //ommitted for brevity } } function writeMap(lat,lng){ var _point = new YGeoPoint(parseInt(lat), parseInt(lng)); var _map = new YMap(document.getElementById('ymap_container')); _map.drawZoomAndCenter(_point, 8); _map.addPanControl( ); _map.addZoomLong( ); document.getElementById('yah_maps').disabled=true; }

A good place to start explaining this program is with the writeMap( ) function. This code shows how easy it is to embed a Yahoo! Map. The code passes the latitude and longitude coordinates into the constructor for a YGeoPoint object (an object provided by Yahoo! Maps). The code then creates a YMap object, specifying the div element that will contain the Yahoo! Map. The next three method calls center the map on the specified coordinates at a certain zoom level (here, 8 in a range of 1 to 16), then add a couple of Yahoo! controls to the map.

The last bit of code disables the Yahoo! Map button, because one embedded map is enough; after loading the map, the user can manipulate it to show any other location.

Driving Directions

The code also contains a function for displaying driving directions:

function getDirections(street,city,state, destName,lat,lng){ var _str = encodeURIComponent(street); var _cit = encodeURIComponent(city); var url = "http://www.parkerriver.com/s/dd?"+_str+"&tlt="+ lat+"&tln="+lng+"&csz="+ _cit+"%2C"+state+"&country=us&tname="+destName; httpRequest("GET",url,true); }

This function is launched when the user clicks the Yahoo! Directions button. (See Figure 4-5 for a view of what this screen and button look like.) The function takes the street, city, and state where the user wants to start the trip, as well as the latitude, longitude, and (optionally) the preferred name for the destination, then sends this information to the server component that actually talks to the Yahoo! application.

The function uses the global JavaScript encodeURIComponent( ) function to make sure that the street and city, which may encompass more than one word (as in "New Orleans"), are properly encoded for an Internet address. In encoding phrases for URLs, New Orleans becomes New%20Orleans, for example.

How Do I Get to Latitude...?

I won't go into great detail about how the server component is programmed, except to say that the address of the component is a Java servlet at http://www.parkerriver.com/s/dd/. The servlet sends an HTTP request to the Yahoo! component, then sifts through the return value for the chunk of HTML representing driving directions. The servlet then sends these directions back to our Ajax application.

Developers can use their API of choice to harvest information from web pages. The servlet in this hack uses APIs from the Java software development kit (SDK), including javax.swing.text.html.parser.ParserDelegator and javax.swing.text.html.HTMLEditorKit.ParserCallback.

geTDirections( ) appends a querystring to the end of the URL following a question mark (?). An example URL is:

http://www.parkerriver.com/s/dd?1%20Main%20St.&tlt=43.96119 0638920&tln=-70.13671875&csz=Smithtown%2CNE&country= us&tname=Main

In other words, the parameters in the querystring represent the origin address and the latitude/longitude of the destination. The server component attaches the Yahoo! application URL (http://api.maps.yahoo.com/dd_result?newaddr=) to the querystring. The servlet then sends an HTTP request to this address, asking Yahoo! for driving directions. This is how the servlet obtains the driving directions for a particular address and map coordinate.

What's Next?

The request object enters the request/response cycle in the way described in "Use Your Own Library for XMLHttpRequest" [Hack #3], using the simple XMLHttpRequest library http_request.js specifies.

The server component scrapes just the chunk of Yahoo!'s response that we plan to usethe driving directions that appear within a div elementand returns this to our JavaScript code. Here's the method that handles the response:

//event handler for XMLHttpRequest function handleResponse( ){ try{ if(request.readyState == 4){ if(request.status == 200){ var _dirs = request.responseText; var targDiv = document.getElementById("ymap_container"); targDiv.innerHTML=_dirs+ '<p><form><button type=\\"button\\" onclick=\\ "window.print( )\\">Print Directions</button></form></p>'; //continued...

The request object returns the div element, itself containing a subset of HTML, in its responseText property. The code then adds this div dynamically to the right side of the browser screen, adding a little code at the end to allow the user to print out the directions.

That's all there is to it! The user can print out the directions by clicking the Print Directions button. The script in handleResponse( ) writes out the code for this button, which just calls window.print( ):

<button type=\\"button\\" onclick=\\"window.print( )\\">

In the spirit of mash-ups, this hack makes the case that two map APIs are better than one.

Категории