OdeToCode IC Logo

Creating a weather gadget using the Weather Channel’s XML Feed

Friday, May 14, 2004

We have all seen various sites with these neat little weather gadgets implemented as server controls, html, etc. Here where I work I was asked to provide a similar component for use with our portal system (Plumtree) that would display for internal and external users the current temperature and forecast for our area.

Being a devoted member of the web services faith and seeing as how that particular architecture fits in well with my other development efforts I started doing some research for free xml feeds of weather data. As it turns out the folks over at the weather channel have a nifty service for this already in place. They require a quick signup and some rules you need to adhere to when providing the service.

The signup process is quick and provides you with 2 key pieces of information: a partner id and a license key. These items are necessary when making your service requests. The link to signup is http://www.weather.com/services/xmloap.html.

The web service kit that you download contains 2 pdf files that describe the XML data structure in some detail and 3 sets of images in png format as well as 2 different logo’s also in png format.

Now that we have all the necessary support components start a new Visual Studio project and create a single web form. Don’t forget to copy the supplied images into the project directory as well. Drop an XML server control onto the form but don’t populate any of the properties as shown in Figure A.

Figure A

In the code behind you will need to add a few libraries:

using System.IO;
using System.Net;
using System.Xml;
using System.Configuration;

The code behind itself is very straight forward. We simply make a web request for the data and load it into an xml document. Once the xml is loaded we can then set our properties for our XML control.

public class DisplayWeatherFeed : System.Web.UI.Page
{
   protected System.Web.UI.WebControls.Xml Xml1;
   private void Page_Load(object sender, System.EventArgs e)
   {
      // Build the request URL
      String reqUrl = "http://xoap.weather.com/weather/local/29206?cc=*&dayf=" + 
        ConfigurationSettings.AppSettings.Get("ExtForecastLength") +  
          "&prod=xoap&par=" + ConfigurationSettings.AppSettings.Get("PartnerID") + 
          "&key=" + ConfigurationSettings.AppSettings.Get("LicenseKey");
      // First we request our content from our provider source .. in this case .. The Weather Channel
      HttpWebRequest wr = (HttpWebRequest)WebRequest.Create(reqUrl);
      //load the response into a response object
      WebResponse resp = wr.GetResponse();
      // create a new stream that can be placed into an XmlTextReader
      Stream str = resp.GetResponseStream();
      XmlTextReader = new XmlTextReader(str);
      reader.XmlResolver = null;
      // create a new Xml document
      XmlDocument doc = new XmlDocument();
      doc.Load(reader);
      // set out object properties
      Xml1.Document = doc;
      Xml1.TransformSource = "~/Weather/WeatherChannelFeed.xslt";
   }
    ...
}

The last line is where the “magic” occurs. This transform file will turn our raw xml data into a world class weather gadget! Ok…but it will be pretty spiffy.

We also need to add some entries to our web.config file shown below:

<appSettings>
   <add key="PartnerID" value="1234567890"/>
   <add key="LicenseKey" value="7ce66aaaabbbbccccdddd" />
   <add key="ExtForecastLength" value="5" />
</appSettings>

These keys identify the PartnerID and LicenseKey you received in an email when you signed up for the xml feed. The last entry specifies how many days forward you wish to receive as part of the feed data. I believe 5 is the maximum. Of those 5 the first day in that extended forecast includes the current day.

This is the code that leverages those settings:

//  Build the request url
String reqUrl = "http://xoap.weather.com/weather/local/29206?cc=*&dayf=" + 
  ConfigurationSettings.AppSettings.Get("ExtForecastLength") +  
    "&prod=xoap&par=" + 
   ConfigurationSettings.AppSettings.Get("PartnerID") + 
    "&key=" + 
   ConfigurationSettings.AppSettings.Get("LicenseKey");

Add a new XSLT file to the project and add the following code to it:

<?xml version="1.0" encoding="ISO-8859-1" ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:template match="/weather">
    <table width="225px" border="0" cellSpacing="0" cellPadding="0" bgcolor="EFF5D7" ID="Table1">
      <tr>
        <td colspan="2" align="center" valign="center" 
            style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: Verdana">
          <xsl:value-of select="loc/dnam"></xsl:value-of>
        </td>
      </tr>
      <tr>
        <xsl:variable name="med-img-dir">./images/weather/64x64</xsl:variable>
        <xsl:variable name="img-ext">png</xsl:variable>
        <xsl:variable name="iconnumber">
          <xsl:value-of select="cc/icon"></xsl:value-of>
        </xsl:variable>
        <td height="64px" width="64px" align="center" valign="center">
          <img src="{$med-img-dir}/{$iconnumber}.{$img-ext}"></img>
        </td>
        <td width="161px" align="center" valign="center" style="FONT-SIZE: 22pt;
                    FONT-WEIGHT: bold">
          <xsl:value-of select="cc/tmp"></xsl:value-of>°F
        </td>
      </tr>
      <tr>
        <td align="center" valign="center" style="FONT-SIZE: 9pt; COLOR: black; 
                    FONT-FAMILY: Verdana; HEIGHT: 33px">
          <xsl:value-of select="cc/t"></xsl:value-of>
        </td>
        <td align="center" valign="center" style="FONT-SIZE: 9pt; COLOR: black; 
                    FONT-FAMILY: Verdana; HEIGHT: 33px">
          Feels like
          <xsl:value-of select="cc/flik"></xsl:value-of>°F
        </td>
      </tr>
      <table width="225px" border="0" cellSpacing="0" cellPadding="0" bgcolor="EFF5D7" ID="Table2">
        <tr>
          <td width="10%"></td>
          <td width="50%" align="left" valign="center" style="FONT-SIZE: 9pt; COLOR: black; 
                      FONT-FAMILY: Verdana">
            UV Index:
          </td>
          <td width="40%" align="left" valign="center" style="FONT-SIZE: 9pt; COLOR: black; 
                      FONT-FAMILY: Verdana">
            <xsl:value-of select="cc/uv/i"></xsl:value-of>
            <xsl:value-of select="cc/uv/t"></xsl:value-of>
          </td>
        </tr>
        <tr>
          <td width="10%"></td>
          <td width="50%" align="left" valign="center" style="FONT-SIZE: 9pt; COLOR: black; 
                      FONT-FAMILY: Verdana">
            Dew Point:
          </td>
          <td width="40%" align="left" valign="center" style="FONT-SIZE: 9pt; COLOR: black; 
                     FONT-FAMILY: Verdana">
            <xsl:value-of select="cc/dewp"></xsl:value-of>°F
          </td>
        </tr>
        <tr>
          <td width="10%"></td>
          <td width="50%" align="left" valign="center" style="FONT-SIZE: 9pt; COLOR: black; 
                      FONT-FAMILY: Verdana">
            Humidity:
          </td>
          <td width="40%" align="left" valign="center" style="FONT-SIZE: 9pt; COLOR: black; 
                      FONT-FAMILY: Verdana">
            <xsl:value-of select="cc/hmid"></xsl:value-of>%
          </td>
        </tr>
        <tr>
          <td width="10%"></td>
          <td width="50%" align="left" valign="center" style="FONT-SIZE: 9pt; COLOR: black; 
                      FONT-FAMILY: Verdana">
            Visibility:
          </td>
          <td width="40%" align="left" valign="center" style="FONT-SIZE: 9pt; COLOR: black; 
                      FONT-FAMILY: Verdana">
            <xsl:value-of select="cc/vis"></xsl:value-of>miles
          </td>
        </tr>
        <tr>
          <td width="10%"></td>
          <td width="50%" align="left" valign="top" style="FONT-SIZE: 9pt; COLOR: black; 
                      FONT-FAMILY: Verdana">
            Pressure:
          </td>
          <td width="40%" align="left" valign="top" style="FONT-SIZE: 9pt; COLOR: black; 
                      FONT-FAMILY: Verdana">
            <xsl:value-of select="cc/bar/r"></xsl:value-of>inches and
            <xsl:value-of select="cc/bar/d"></xsl:value-of>
          </td>
        </tr>
        <tr>
          <td width="10%"></td>
          <td width="50%" align="left" valign="top" style="FONT-SIZE: 9pt; COLOR: black; 
                     FONT-FAMILY: Verdana">
            Wind:
          </td>
          <td width="40%" align="left" valign="top" style="FONT-SIZE: 9pt; COLOR: black; 
                      FONT-FAMILY: Verdana">
            <xsl:value-of select="cc/wind/s"></xsl:value-of>mph
          </td>
        </tr>
      </table>
      <table width="225px" border="0" cellSpacing="0" cellPadding="0" bgcolor="EFF5D7" ID="Table3">
        <tr>
          <td colspan="5" width="100%" align="center"><u>Extended Forecast</u></td>
        </tr>
        <tr>
          <xsl:for-each select="dayf/day">
            <xsl:if test="@d > 0">
              <td width="25%" align="center" valign="top" style="FONT-SIZE: 7pt; COLOR: black; 
                          FONT-FAMILY: Verdana">
                <xsl:value-of select="@t"></xsl:value-of>
              </td>
            </xsl:if>
          </xsl:for-each>
        </tr>
        <tr>
          <xsl:variable name="small-img-dir">./images/weather/32x32</xsl:variable>
          <xsl:variable name="img-ext">png</xsl:variable>
          <xsl:for-each select="dayf/day">
            <xsl:if test="@d > 0">
              <xsl:variable name="iconnumber">
                <xsl:value-of select="part/icon"></xsl:value-of>
              </xsl:variable>
              <td width="25%" align="center" valign="center">
                <img border="1" src="{$small-img-dir}/{$iconnumber}.{$img-ext}"></img>
              </td>
            </xsl:if>
          </xsl:for-each>
        </tr>
        <tr>
          <xsl:for-each select="dayf/day">
            <xsl:if test="@d > 0">
              <td width="25%" align="center" valign="top" style="FONT-SIZE: 7pt; COLOR: black; 
                          FONT-FAMILY: Verdana">
                High:
                <xsl:value-of select="hi"></xsl:value-of>
              </td>
            </xsl:if>
          </xsl:for-each>
        </tr>
        <tr>
          <xsl:for-each select="dayf/day">
            <xsl:if test="@d > 0">
              <td width="25%" align="center" valign="top" style="FONT-SIZE: 7pt; COLOR: black; 
                          FONT-FAMILY: Verdana">
                Low:
                <xsl:value-of select="low"></xsl:value-of>
              </td>
            </xsl:if>
          </xsl:for-each>
        </tr>
        <tr>
          <xsl:for-each select="dayf/day">
            <xsl:if test="@d > 0">
              <td width="25%" align="center" valign="top" style="FONT-SIZE: 7pt; COLOR: black; 
                          FONT-FAMILY: Verdana">
                <xsl:value-of select="part/t"></xsl:value-of>
              </td>
            </xsl:if>
          </xsl:for-each>
        </tr>
      </table>
      <table width="225px" border="0" cellSpacing="0" cellPadding="4" bgcolor="EFF5D7" ID="Table4">
        <tr>
          <xsl:variable name="twclink">http://www.weather.com/?prod=xoap&par=1004980284
          </xsl:variable>
          <td width="20%" align="center" valign="top" style="FONT-SIZE: 7pt; COLOR: black;
                      FONT-FAMILY: Verdana">
            Weather data provided by<a href="{$twclink}" target="_none">
              <img border="0" src="./images/weather/logos/TWClogo_32px.png"></img></a>
          </td>
        </tr>
      </table>
    </table>
  </xsl:template>
</xsl:stylesheet>

Now that we have our transform done we can see what our finished gadget will look like. I expanded on the original request slightly by adding an extended forecast to the bottom of the current day’s forecast.

Some enhancements that could immediately be made are to:

1) Allow users to have a configurable version. The version I wrote is only for the local area so user configurability wasn’t a concern. For sites with users all over a configurable version would be useful.

2) Making this into a control.

The xslt and html code could use some polishing as well. I wrote this from start to finish in about 3 hours so elegance wasn’t on the menu at the time. I am a firm believer in polished, neat code so at some point I will go back and practice the preachin but for now I have other pressing projects that need attention.

by Travis Cotton .