Display weather information on a web page and search a different location without a page submission. This hack displays detailed weather information for a location, and allows the user to search another U.S. location for its temperature, humidity, and other weather-related data. The web page displays the new weather report without a complete page refresh. The information is derived from The Weather Channel Interactive, Inc. (http://www.weather.com). PreppingTo use the Weather Channel's XML data feed in a hack, you have to sign up at Weather.com and download the software development kit. The SDK contains some logos and a couple of PDF guides explaining the requirements for usage of the data. If you want to implement this data feed, the signup begins at http://www.weather.com/services/xmloap.html (the URL is not a typo!). This hack sends the name of a U.S. city and state to a Weather.com URL that implements a web service. As part of the usage requirements, a registered developer must send along a partner ID and license ID as parameters in the URL. Weather.com responds with an XML file containing detailed weather information for the specified location. Figure 4-6 shows what the page looks like in Firefox 1.5. Figure 4-6. Weather for our default location
When the browser loads the web page, the weather report for a default location is loaded into it. The user can then enter a city name, select a state, and then request the weather data for a new location. Here are highlights of the web page, which imports a couple of JavaScript libraries: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/2000/REC-xhtml1-20000126/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <head> <meta http-equiv="content-type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="/books/4/254/1/html/2/js/http_request.js" /> <script type="text/javascript" src="/books/4/254/1/html/2/js/hacks_4_4.js" /> <link rel="stylesheet" type="text/css" href="/css/hacks.css" /> <title>Get Your Local Weather</title> </head> <body> <div > <span ></span><br /> <span ></span><br /> <span ></span><br /> <span ></span><br /> <span ></span><br /> <span ></span> </div> <h3>Your weather today: <span ></span></h3> <div > <img src="/books/4/254/1/html/2/" width="64" height="64" align="left"/> <span ></span><br /> <span ></span><br /> <span ></span><br /> <span ></span><br /> <span ></span><br /> <span ></span><br /> <span ></span> </div> <h4>New Location</h4> <div > <form action="javascript:void%200" > <table border="0"><tr> <td>City/Town: <input type="text" name= "_city" size="15" maxlength="15" /></td></tr> <tr><td>State: <select name="_state"> <option value="al">Alabama</option> <option value="ak">Alaska</option> <option value="az">Arizona</option> <option value="ar">Arkansas</option> <option value="ca">California</option> <!-- SNIPPED --> </select></td></tr> <tr><td><button type="button" name= "go" value="Go">Get Weather</button> </td></tr> <!SNIPPED --> </html> Two div elements contain the weather data that you load in from an XML file. The bottom of the page contains a form for entering in the new location and requesting more weather info. The real guts of the application are represented by the JavaScript in hacks_4_4.js. http_request.js "Use Your Own Library for XMLHttpRequest" [Hack #3] handles the XMLHttpRequest object. Here's the code in hacks_4_4.js. A window.onload event handler gets things going once the browser has finished loading the XHTML code: var defaultLocation; var part; var lic; var _host="www.parkerriver.com" //city and state of location user searched for var _cit = ""; var _stat = ""; window.onload = function( ){ document.getElementById("getWeather").onclick=function( ){ getNewLocation( ); } getWeather(defaultLocationId); } /* Get the weather XML data for a certain location */ function getWeather(locationId){ if (locationId == null || locationId.length=="") { return; } var url = "http://"+_host+"/s/weathxml/weatherSearch?&loc&cc=*&dayf=2&prod=xoap&par="+ partId+"&key="+licId; httpRequest("GET",url,true,handleResponse); } function getNewLocation( ){ var val = document.forms[0]._city.value; if(val.length != 0){ _cit = val; } else { //we need at least a city to do a search return; } var sval = document.forms[0]._state.value; if(sval.length != 0){ _stat = sval; getLocation(_cit+","+_stat); } else { getLocation(_cit); //We can do a search with only a city name } } /* The parameter can be a city alone or a city,state combo as in Boston,MA */ function getLocation(_lcity){ if (_lcity == null || _lcity.length=="") {alert("returning"); return; } //server component URL; the component connects with Weather.com var url = "http://"+_host+"/s/weathxml/addressSearch?city="+_lcity; httpRequest("GET",url,true,handleResponse); } //event handler for XMLHttpRequest function handleResponse( ){ try{ if(request.readyState == 4){ if(request.status == 200){ var _xml = request.responseXML; if(_xml != null){ var _root = _xml.documentElement; switch(_root.tagName){ case "weather": displayWeather(_root); break; case "search": handleSearchResult(_root); break; case "error" : alert("Your weather or location search "+ "generated an error. "+ "Please try again."); break; default: alert("Your search generated an "+ "unspecified problem. "+ "Please try again."); } } else { alert("The server returned a null value "+ "for the XML. Please try again in a few seconds."); } } else { //See Hack #3... } }//end outer if } catch (err) { //See Hack #3... } } /* Display the weather based on XML data derived from the Weather.com API */ function displayWeather(rootElement){ if(rootElement != null){ var loc= rootElement.getElementsByTagName("loc")[0]; setupToplevel(loc); var dayf = rootElement.getElementsByTagName("dayf")[0]; setupWeather(dayf); } } function handleSearchResult(rootEl){ var locArray = rootEl.getElementsByTagName("loc"); var elVal = null; for(var i = 0; i < locArray.length; i++){ elVal = locArray[i].firstChild.nodeValue; //if a state was specified in the search, include in //the search here if(_stat.length != 0){ if (elVal == _cit+", "+_stat.toUpperCase( )) { getWeather(locArray[i].getAttribute("id")); } } else { alert("No state in search."); //just return the first result if no state is provided getWeather(locArray[i].getAttribute("id")); break; } } } /* Pull data from the XML and plug it into the proper span tag in the XHTML */ function setupToplevel(_element){ if(_element != null){ setupElement( _element.getElementsByTagName("dnam")[0], document.getElementById("city_state"),"Location"); setupElement( _element.getElementsByTagName("tm")[0], document.getElementById("time"),"Time"); setupElement( _element.getElementsByTagName("lat")[0], document.getElementById("lat"),"Lat"); setupElement( _element.getElementsByTagName("lon")[0], document.getElementById("lng"),"Long"); setupElement( _element.getElementsByTagName("sunr")[0], document.getElementById("sunrise"),"Sunrise"); setupElement( _element.getElementsByTagName("suns")[0], document.getElementById("sunset"),"Sunset"); } } function setupElement(_node,_span,txtMsg) { if(arguments.length == 3){ _span.innerHTML= txtMsg+": "+_node.firstChild.nodeValue; } else { _span.innerHTML= _node.firstChild.nodeValue; } } //embed the weather image function setupImgElement(_node,_imgElement) { _imgElement.src="/books/4/254/1/html/2/http://"+_host+"/ajaxhacks/img/"+ _node.firstChild.nodeValue+".png"; } function setupWeather(_element){ if(_element != null){ var parts = _element.getElementsByTagName("part"); /* Contains sub-elements describing day/night weather */ var dpart = null; setupElement( _element.getElementsByTagName("lsup")[0], document.getElementById("date")); setupElement( _element.getElementsByTagName("hi")[0], document.getElementById("high"),"high temp"); setupElement( _element.getElementsByTagName("low")[0], document.getElementById("low"),"low temp"); for(var i = 0; i < parts.length; i++) { if(parts[i].getAttribute("p") == "d") { dpart=parts[i];} } setupImgElement( dpart.getElementsByTagName("icon")[0], document.getElementById("w_icon")); setupElement(dpart.getElementsByTagName("ppcp")[0], document.getElementById("precip"),"precipitation (% chance)"); setupElement( dpart.getElementsByTagName("hmid")[0], document.getElementById("humid"),"humidity (%)"); setupElement(dpart.getElementsByTagName("t")[0], document.getElementById("desc")); var _wind = dpart.getElementsByTagName("wind")[0]; setupElement( _wind.getElementsByTagName("s")[0], document.getElementById("spd_wind"),"wind speed"); setupElement( _wind.getElementsByTagName("t")[0], document.getElementById("dir_wind"),"wind direction"); } } Most of this code involves pulling the content out of the returned XML and displaying it on the web page. Two functions request weather data for a location and search for a "location ID" associated with a city/state combination, such as Oakland, CA. To access this weather XML feed, the requestor has to provide a location ID in the URL, representing a city or city/state combination. If the user provides a city and/or state for weather information, our application has to request the location ID first (we already know the location ID for our default location), then use this ID to fetch its weather data: /* Get the weather XML data for a certain location */ function getWeather(locationId){ if (locationId == null || locationId.length=="") { return; } var url = "http://"+_host+"/s/weathxml/weatherSearch?&loc&cc=*&dayf=2&prod=xoap&par="+ partId+"&key="+licId; httpRequest("GET",url,true,handleResponse); } function getNewLocation( ){ var val = document.forms[0]._city.value; if(val.length != 0){ _cit = val; } else { //we need at least a city to do a search return; } var sval = document.forms[0]._state.value; if(sval.length != 0){ _stat = sval; getLocation(_cit+","+_stat); } else { getLocation(_cit); //we can do a search with only a city name } } /* The parameter can be a city alone or a city,state combo as in Boston,MA */ function getLocation(_lcity){ if (_lcity == null || _lcity.length=="") {alert("returning"); return; } //server component URL; the component connects with Weather.com var url = "http://"+_host+"/s/weathxml/addressSearch?city="+_lcity; httpRequest("GET",url,true,handleResponse); } The URL points to a server component you use to connect with Weather.com's web service.
This hack uses a Java servlet that implements a different Weather.com request based on the path info of the request that the servlet receives. The path info comprises the characters in a URL following the path to the server component, but preceding the querystring, as in addressSearch in the following URL:
The servlet, if its handshake with Weather.com is successful, grabs and returns to our application a pretty big XML file representing the weather information. Here is an example of the XML returned from Weather.com: <!-- top-level: time. lat-long,sunrise, sunset--> <loc > <dnam>Marietta, GA (30066)</dnam> <tm>10:40 AM</tm> <lat>34.04</lat> <lon>-84.51</lon> <sunr>7:02 AM</sunr> <suns>6:37 PM</suns> <zone>-5</zone> </loc> <!-- daily forecast: --> <dayf> <lsup>3/5/03 9:50 AM EST</lsup> <day d="0" t="Wednesday" dt="Mar 5"> <hi>64</hi> <low>54</low> <sunr>7:02 AM</sunr> <suns>6:37 PM</suns> <part p="d"> <icon>26</icon> <t>Sprinkles</t> <wind> <s>10</s> <gust>N/A</gust> <d>0</d> <t>W</t> </wind> <ppcp>20</ppcp> <hmid>77</hmid> </part> <part p="n"> <icon>47</icon> <t>Scattered T-Storms</t> <wind> <s>13</s> <d>0</d> <t>SW</t> </wind> <ppcp>60</ppcp> <hmid>77</hmid> </part> </day> </dayf> This is the type of XML content that the servlet returns to your application when you already know the location ID for a certain city and state. The XML even includes an icon element so that your page can display a Weather.com image representing the weather conditions. |