You are here: Home > Articles > Article Display

A ColdFusion weather custom tag

This custom tag will allow you to easily add a weather control to your website. Using the XML data feeds from weather.com we can produce owesome looking weather controls, both for current conditions and for forecasts. This control has a multitude of attributes, is fully customizable, easy to use, comes with example templates, and full source code provided.

Published: Sep 15, 2005
Tested with: ColdFusion MX 6.1, ColdFusion 7.0
Category: ColdFusion
22,360 views

Introduction

XML Data Feeds are very popular and useful. One of them, the weather.com XML Data Feed, allows third-party applications to access a subset of the data available on the weather.com website via an on-request XML feed. The Weather Channel Interactive, Inc. (TWCi) provides this data free of charge to developers who wish to incorporate weather information into a single application in exchange for links back to weather.com.

This article describes how to access and use the XML feed in order to add a weather control on your website. I will try to do this by focusing on the following:

  • how to access the XML feeds from weather.com
  • how to call the custom tag (mandatory/optional tag parameters)
  • what is available to us once we have properly created the tag
  • how to customize the look and feel to your liking

What this article is not going to explain is how to build a custom tag. The source code is freely available to you to do so as you please, so feel free to go though it and see how it works. A separate forum has been set up for this article to make it easier to discuss any questions/advice/suggestions you may have.

Signing up at weather.com

Even though this service is free to use, you will need to sign up and get a subscriber account to use it. There are rules to adhere to for using this service, like how often you should cache your feeds, displaying their logo and a link back to them on your site, etc. You should take the time and read them after you sign up to avoid lockout issues.

The sign up process is fairly quick and easy. At the end of the registration process you will receive an e-mail message containing your Partner ID, unique License Key, and download instructions for the Software Developers Kit (SDK). The Partner ID and License Key must be included as query parameters for all XML weather requests.

The SDK contains:

  1. 2 PDF files that outline the rules of use of the service and instructions on how to call it.
  2. All the images that you could need to add to your control for the current weather conditions, in 3 sizes: 32x32, 64x64 and 128x128 pixels.
  3. 2 weather.com logos: 32x32 and 64x64 pixels.

Getting your location ID

No matter which location you are at in the world, weather.com should have it in their database. They have their own way of storing location codes though (or IDs), so they make available a separate feed upon which we can send requests to find out the ID of our location. For example, let's say that you are looking for Athens:
http://xoap.weather.com/search/search?where=athens
The result would be this XML file:

For illustration purposes, let's suppose that what you were after is Athens, Greece, with location ID GRXX0004.

To make this task easier for you, I have included in the download package a file called "location_search.cfm". This is a form that lets you enter a query in a text field and takes care of posting to the feed above for location IDs, presenting the results in a nicer way. Running this file against the "athens" search would produce the following:

Search for location ID through form

Same thing, just more friendly. :o)

Please note that the code for this form does a "cfhttp" request. If you are behind a firewall, you will need to edit the code to add at least the "proxyServer" and "proxyPort" attributes to the call to make it work.

Requesting the XML Data Feed

The URL parameters for the XML feed are:

Query Parameter Usage
cc Current Conditions
OPTIONAL
VALUE IGNORED
dayf Multi-day forecast information
OPTIONAL
VALUE = [ 1..10 ]
link Links for weather.com pages
OPTIONAL
VALUE = xoap
par Partner ID assigned to you
REQUIRED
VALUE = {partner id}
prod The XML Server product code
REQUIRED
VALUE = xoap
key The license key assigned to you
REQUIRED
VALUE = {license key}
unit Set of units. Standard or Metric
OPTIONAL
VALUES = [ s | m ]
DEFAULT = s

For example:
http://xoap.weather.com/weather/local/GRXX0004?cc=*&prod=xoap&par=[partner_id]&key=[license_key]&unit=s&dayf=10
This would return the current conditions in standard (American) units for Athens, Greece, as well as forecast infrormation for the next 10 days. Here's a snapshot of the returned XML file (some nodes are contracted):

Example of xml data feed

Using the custom tag

All the code you will need is inside a single file, the "weather.cfm" in the downloaded zip file, which acts as the custom tag. Follow the same procedure as you normally do when you store custom tags on your server: place it either in a special mapped folder, or in the same folder as the file that you are calling it from.

<cf_weather> attributes
Name Default Mandatory Description
locationCode   Yes The location ID of the place where the control is for.
partnerID   Yes Your Partner ID given to you by weather.com after registering.
licenseKey   Yes Your license key given to you by weather.com after registering.
imageLocation /weather/images/ No The virtual path to the weather.com images and logos.
Make sure you include the last "/".
imageSize medium No

Acceptable values:

  1. small
  2. medium
  3. large

small = 32x32 pixels
medium = 64x64 pixels
large = 128x128 pixels

imageType png No

Acceptable values:

  1. png
  2. gif

The weather.com SDK provides all the images in PNG format. These PNGs have a transparent background, which does not display in IE 6.x and below. To work around this, I added a GIF duplicate of each PNG file so that you can choose which one you want to have displayed. Also, in the downloadable examples you will notice that I added to the pages a Javascript "hack" which displays PNGs fine even in IE. Thank you Bob Osola!

daysForecast 10 No

Acceptable values:

  • 1-10
xmlFolder #ExpandPath('.')# No The folder where the cached XML data feed file will be saved to. It defaults to the same folder as the one that you are calling it from, but you can place it anywhere on the server. The path needs to be absolute, i.e. "C:\wwwroot\website".
xmlFile weather No The name that will be given to the XML file.
unit s No

Acceptable values:

  1. s
  2. m

s = Standard, or American
m = Metric, or everybody else!

cacheSeconds 3600 No

Acceptable values:

  • >= 0

The number in seconds that the local XML file will be cached. Once this expires, the tag will call the XML data feed again the next time it is called. There is no need to set it any less than one hour, the feeds to not get refreshed that often anyway.

proxyServer   No In case you are behind a firewall you might need to add proxy settings to do a HTTP GET request for the feed.
proxyPort   No In case you are behind a firewall you might need to add proxy settings to do a HTTP GET request for the feed.
proxyUser   No In case you are behind a firewall you might need to add proxy settings to do a HTTP GET request for the feed.
proxyPassword   No In case you are behind a firewall you might need to add proxy settings to do a HTTP GET request for the feed.

Armed with the knowledge of all available attributes of this tag, the simplest way of calling it:

1 <cf_weather locationCode="GRXX0004" partnerID="xxxxxxxxxx" licenseKey="xxxxxxxxxxxxxxxx">
2     ...
3 </cf_weather>

Between the opening and closing of the tag, we have access to an all-powerful structure called "sWeather". This structure has all the information from the XML feed stored in it. Populating a local variable this way from the custom tag proves to be very powerful in this case. The reason is that we can now write our own HTML/CF code inside the tags to create our custom look and feel of the weather control. Go ahead and try it: do a <cfdump> of this structure within the tags and see what you get:

1 <cf_weather locationCode="GRXX0004" partnerID="xxxxxxxxxx" licenseKey="xxxxxxxxxxxxxxxx">
2     <cfdump var="#sWeather#">
3 </cf_weather>

Taking a look at the resulting <cfdump>, we see 4 nested structures:

sWeather structure

  1. sCurrentConditions
    Used mostly for outputting current conditions
  2. sForecast
    Nested information for each of the days in the forecast
  3. sParams
    These are the custom and default attributes of the tag
  4. sProperties
    General display properties for the custom tag, e.g. units of measurement

Current Conditions example

OK! Now let's see an actual example of how to use the tag to produce the current conditions of Athens, Greece. Create a new CFM file and add the following code to it:

1 <style type="text/css">
2 .currentWeather {
3     background-color:#EFF5D7;
4     color:#3A5029;
5     font-family:Tahoma;
6     font-size:70%;
7     border:1px solid #CAD3A4;
8     width:250px;
9 }
10 .currentWeather table {
11     font-size:100%;
12     color:#3A5029;
13 }
14 .currentWeather .location {
15     background-color:#BED273;
16     padding:5px 10px;
17     font-weight:bold;
18     text-align:center;
19 }
20 .currentWeather .icon {
21     text-align:center;
22 }
23 .currentWeather .temperature {
24     font-size:280%;
25     padding-left:25px;
26     text-align:center;
27 }
28 .currentWeather .description {
29     font-weight:bold;
30 }
31 .currentWeather .feelslike {
32     font-weight:bold;
33     padding-left:25px;
34 }
35 .currentWeather .lastupdate {
36     padding:10px;
37 }
38 </style>
39     
40 <script language="javascript">
41 // Correctly handle PNG transparency in Win IE 5.5 or higher.
42 // http://homepage.ntlworld.com/bobosola/
43 function correctPNG() {
44     for (var i=0; i<document.images.length; i++) {
45         var img = document.images[i]
46         var imgName = img.src.toUpperCase()
47         if (imgName.substring(imgName.length-3, imgName.length) == "PNG") {
48             var imgID = (img.id) ? "id='" + img.id + "' " : "";
49             var imgClass = (img.className) ? "class='" + img.className + "' " : "";
50             var imgTitle = (img.title) ? "title='" + img.title + "' " : "title='" + img.alt + "' ";
51             var imgStyle = "display:inline-block;" + img.style.cssText;
52             if (img.align == "left") imgStyle = "float:left;" + imgStyle;
53             if (img.align == "right") imgStyle = "float:right;" + imgStyle;
54             if (img.parentElement.href) imgStyle = "cursor:hand;" + imgStyle;
55             var strNewHTML = "<span " + imgID + imgClass + imgTitle
56                 + " style=\"" + "width:" + img.width + "px; height:" + img.height + "px;" + imgStyle + ";"
57                 + "filter:progid:DXImageTransform.Microsoft.AlphaImageLoader"
58                 + "(src=\'" + img.src + "\', sizingMethod='scale');\"></span>";
59             img.outerHTML = strNewHTML;
60             i = i-1;
61         }
62     }
63 }
64 window.attachEvent("onload", correctPNG);
65 </script>
66     
67 <cf_weather locationCode="GRXX0004" partnerID="xxxxxxxxxx" licenseKey="xxxxxxxxxxxxxxxx">
68     <cfoutput>
69     <table border="0" cellpadding="0" cellspacing="0" class="currentWeather">
70         <tr>
71             <td class="location">#sWeather.sLocation.city#</td>
72         </tr>
73         <tr>
74             <td align="center">
75                 <br />
76                 <table border="0" cellpadding="0" cellspacing="0">
77                     <tr>
78                         <td class="icon"><img src="#sWeather.sParams.imageLocation & sWeather.sParams.imageSize & "x" & sWeather.sParams.imageSize & "/" & sWeather.sCurrentConditions.icon & "." & sWeather.sParams.imageType#" border="0" alt="#sWeather.sCurrentConditions.description#"></td>
79                         <td class="temperature">#sWeather.sCurrentConditions.temperature#&##176;#sWeather.sProperties.temperature#</td>
80                     </tr>
81                     <tr>
82                         <td class="description">#sWeather.sCurrentConditions.description#</td>
83                         <td class="feelslike">Feels like #sWeather.sCurrentConditions.feelslike#&##176;#sWeather.sProperties.temperature#</td>
84                     </tr>
85                 </table>
86             </td>
87         </tr>
88         <tr>
89             <td align="center">
90                 <br />
91                 <br />
92                 <table border="0" cellpadding="1" cellspacing="0">
93                     <tr>
94                         <td align="right" valign="top">UV index:</td>
95                         <td valign="top">#sWeather.sCurrentConditions.uvindex# #sWeather.sCurrentConditions.uvDescription#</td>
96                     </tr>
97                     <tr>
98                         <td align="right" valign="top">Dew Point:</td>
99                         <td valign="top">#sWeather.sCurrentConditions.dewpoint#&##176;#sWeather.sProperties.temperature#</td>
100                     </tr>
101                     <tr>
102                         <td align="right" valign="top">Humidity:</td>
103                         <td valign="top">#sWeather.sCurrentConditions.humidity#%</td>
104                     </tr>
105                     <tr>
106                         <td align="right" valign="top">Visibility:</td>
107                         <td valign="top">#sWeather.sCurrentConditions.visibility# #sWeather.sProperties.distance#</td>
108                     </tr>
109                     <tr>
110                         <td align="right" valign="top">Pressure:</td>
111                         <td valign="top">#sWeather.sCurrentConditions.barIndex# #sWeather.sProperties.pressure# (#sWeather.sCurrentConditions.barDescription#)</td>
112                     </tr>
113                     <tr>
114                         <td align="right" valign="top">Wind:</td>
115                         <td valign="top">From the #sWeather.sCurrentConditions.winddirection# at #sWeather.sCurrentConditions.windspeed# #sWeather.sProperties.speed#</td>
116                     </tr>
117                 </table>
118             </td>
119         </tr>
120         <tr>
121             <td class="lastupdate" align="center">
122                 <br />
123                 As reported at #sWeather.sCurrentConditions.station#, on
124                 <cfset variables.cLastUpdate = Mid(sWeather.sCurrentConditions.lastUpdate, 1, Len(sWeather.sCurrentConditions.lastUpdate) - 10)>
125                 <cfset variables.cLastUpdateDate = DateFormat(variables.cLastUpdate, "full")>
126                 <cfset variables.cLastUpdateTime = TimeFormat(variables.cLastUpdate, "h:mm tt")>
127                 #variables.cLastUpdateDate# at #variables.cLastUpdateTime#
128                 local time<br />
129                 <br />
130                 <a href="http://www.weather.com"><img src="#sWeather.sParams.imageLocation#logos/TWClogo_32px.png" border="0" alt="Data provided by weather.com" title="Data provided by weather.com"></a>
131             </td>
132         </tr>
133     </table>
134     </cfoutput>
135 </cf_weather>

That's a lot of code but not that complex really. What's happening here? First, we declare some CSS styles to pretty up our display. Then it's that IE PNG Javascript fix that I talked about. We then call our custom tag, passing in as arguments the location ID, the partner ID and the license key. The rest of the code nested inside the tag is simply outputting parts of the sWeather structure to create our display. The result is a nice looking weather control:

Weather forecast example

Since the code to generate a forecast is pretty much the same as the one used for generating the current conditions, I am not going to post it again. Besides, all this code is included in the downloadable zip file. Suffice to say that the example I have included in the downloads will produce the following display:

Weather forecast example

Application and Session scopes

As forum user cgirtman correctly points out, application and session scopes must be turned on for this tag to work. I have included an "Application.cfm" file together with the downloads, which shows you what you need. You can leave this file inside your weather folder, or enable these scopes at the root level - that's up to you. All you need really, is one line of code:

1 <CFAPPLICATION NAME="sWeather" SESSIONMANAGEMENT="Yes">

A word about caching

You could make your weather control even faster by caching its output HTML into the application scope and refreshing it at specific intervals. There are many custom tags out there that do this, and all you have to do is simply wrap their custom tag around mine. An example that I use on my projects is cf_accelerate by Brandon Purcell.

A call to arms!

Don't feel restricted by my designs above. Feel free to experiment with your own, by adding/removing elements from the display and changing the look and feel. I would love to see some different versions of your own, so if you do produce any please send them to me. I will make sure to post a link here to your control, and maybe include your code version in the downloadable materials - if you approve of course. You can use the dedicated forum for this article to post your links and code.

Conclusion

In this article we saw how to use a custom tag of my own to add a weather control to your website. There are not many open source ColdFusion weather controls out there, and that was my main motivation for creating one. The article focused more on how the weather.com data feeds work, and how to use the custom tag, rather than how to write it.

 



Other articles in this category
  1. A Coldfusion color picker custom tag
    June 27, 2005
    We'll learn how to create and use a Coldfusion custom tag, that will add a color picker field to a form and make it easy to select a web safe color. No popups were harmed during the making of this article!
  2. Firefox download counter in Coldfusion
    June 21, 2005
    We'll see how to create and use a Coldfusion custom tag that will make it easy for us to display the Firefox download counter.
  3. Working with cfpop email headers
    March 31, 2005
    Using the cfpop tag to retrieve emails returns the headers as one long text string, which makes it difficult to get specific values. In this article we'll see first how to get the headers, and then how to work with them to get the name-value pair we want.