| |
Using Weather Forecast XML from the
National Weather Service
Back to Geek Stuff
For the weather information on the home page, the source
data comes from:
Key components:
| /include/noaa.asp |
Include file containing functions that get the
XML and perform the transformations. (see
more) |
| /weather/forecastNoaa.asp |
This page displays the forecast information |
| /xslt/forecast.xslt |
This XSL stylesheet is used to take the forecast
XML and create the HTML used in forecastNoaa.asp (see
more) |
| /weather/storms.asp |
This page displays the latest storm information
from the National Hurricane Center |
| /xslt/storminfo.xslt |
This XSL stylesheet is used to take the NHC
XML and create the HTML used in storms.asp |
| /xslt/currentconditions.xslt |
This XSL stylesheet is used to take the current
conditions XML and create the HTML displayed on the home
page. (see more) |
For the current conditions, I use this code:
function transformCurrent()
' You need to enter the full path to a XML file in same secure authorized directory
' as your Web virtual directory where this ASP page is loaded from and executed.
xmlFile = "http://www.nws.noaa.gov/data/current_obs/KRDU.xml"
xsltSheet = server.mappath("/xslt/currentconditions.xslt")
Dim root
Dim xmlDoc
dim xsltDoc
Dim child
Dim indent
indent=0
Set xmlDoc = createXmlDomDocument(xmlDoc)
set xsltDoc = createXMLDomDocument(xsltDoc)
xmlDoc.async = False
xmlDoc.validateOnParse=False
xmlDoc.setProperty "ServerHTTPRequest", true
xmlDoc.load xmlFile
'xmlDoc.open "GET", xmlFile, False, "", ""
If xmlDoc.parseError.errorcode = 0 Then
Else
Response.Write("<P>There was an error in : " & xmlFile &"</P><P>Line: " & _
xmlDoc.parseError.line & _
"<BR>Column: " & xmlDoc.parseError.linepos & "</P>" & _
xmlDoc.parseError.reason)
End If
xsltDoc.validateOnParse=False
xsltDoc.load xsltSheet
transformCurrent = xmlDoc.transformNode(xsltDoc)
transformCurrent = replace(transformCurrent, "From the", "")
end function
Function createXmlDomDocument(xd)
On Error Resume Next
'Uncomment the following line to use MSXML 3.0
Set xd = CreateObject("MSXML2.DOMDocument.3.0")
'Uncomment the following line to use MSXML 4.0
'Set xd = CreateObject("MSXML2.DOMDocument.4.0")
'Uncomment the following line to use MSXML 5.0
'Set xd = CreateObject("MSXML2.DOMDocument.5.0")
If (IsObject(xd) = False) Then
alert("DOM document not created. Check MSXML version used in createXmlDomDocument.")
Else
Set createXmlDomDocument = xd
End If
End Function
function getCurrentConditionsXML(searchType)
Set objInputXMLDoc = Server.CreateObject("Microsoft.XMLDOM")
objInputXMLDoc.load Server.MapPath("noaarequest.xml")
objInputXMLDoc.selectSingleNode("//product").Text = searchType
'
' Post the SOAP message.
'
Set objXMLHTTP = Server.CreateObject("Microsoft.XMLHTTP")
objXMLHTTP.open "post", "http://www.nws.noaa.gov/forecasts/xml/SOAP_server/ndfdXMLserver.php", False
objXMLHTTP.setRequestHeader "Content-Type", "text/xml"
objXMLHTTP.setRequestHeader "SOAPAction", "NDFDgenResponse"
objXMLHTTP.send objInputXMLDoc
' Dump the results into an XML document.
Set objOutputXMLDoc = Server.CreateObject("Microsoft.XMLDOM")
objOutputXMLDoc.loadXML objXMLHTTP.responseText
' Assign the node to the new document, and voila! You have the XML from the SOAP document in it's own doc
Set Node = objOutputXMLDoc.selectSingleNode("//xmlOut")
' Return XML text for xmlOut node to be used as input to forecast xslt
getCurrentConditionsXML = Node.text
end function
function transformForecast()
dim forecastXML
dim xmlDoc
dim xsltDoc
dim xsltSheet
xsltSheet = server.mappath("/xslt/forecast.xslt")
xmlFile = server.mappath("noaadump.xml")
forecastXML = getCurrentConditionsXML("glance")
' document with transform info
set xsltDoc = createXMLDomDocument(xsltDoc)
' document with XML from SOAP request
set xmlDoc = createXMLDomDocument(xmlDoc)
xsltDoc.validateOnParse = false
xsltDoc.load xsltSheet
' load the forecast information using the XML returned by the SOAP request
xmlDoc.loadXML forecastXML
transformForecast = xmlDoc.transformNode(xsltDoc)
end function |
...the XSL transform used is getCurrentConditions.xslt below...
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="html"/> <xsl:template match="/"> <xsl:element name="table"> <xsl:element name="tr"> <xsl:element name="td"> <xsl:attribute name="class">c5_bold</xsl:attribute>Weather at <xsl:value-of select="//location"/> </xsl:element> </xsl:element> <xsl:element name="tr"> <xsl:element name="td">Current temperature: <xsl:value-of select="//temperature_string"/> </xsl:element> </xsl:element> <xsl:element name="tr"> <xsl:element name="td"> <xsl:value-of select="//weather"/> </xsl:element> </xsl:element> <xsl:element name="tr"> <xsl:element name="td">Relative humidity: <xsl:value-of select="//relative_humidity"/>% </xsl:element> </xsl:element> <xsl:element name="tr"> <xsl:element name="td">Wind: <xsl:value-of select="//wind_string"/> </xsl:element> </xsl:element> <xsl:element name="tr"> <xsl:element name="td">Heat Index: <xsl:value-of select="//heat_index_string"/> </xsl:element> </xsl:element> <xsl:element name="tr"> <xsl:element name="td">Courtesy: <xsl:element name="a"> <xsl:attribute name="href"><xsl:value-of select="//credit_URL"/></xsl:attribute> <xsl:attribute name="alt"><xsl:apply-templates select="//credit"/></xsl:attribute> <xsl:value-of select="//credit"/> </xsl:element> </xsl:element> </xsl:element> </xsl:element> </xsl:template> <!-- <xsl:template match="/temperature_string"> Current temperature: <xsl:value-of select="."/> </xsl:template> <xsl:template match="/location">
Location: <xsl:value-of select="."/>
</xsl:template>-->
</xsl:stylesheet>
|
For the forecast, I use this xsl stylesheet, forecast.xslt. It
generates most of the content of the forecast page from the results
of the SOAP request above.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="html"/> <!-- initialize keys --> <xsl:key name="layout-key" match="data/time-layout" use="layout-key"/> <xsl:key name="day-key" match="data/time-layout/start-valid-time" use="substring(., 1, 10)"/> <!-- initialize variables --> <!-- get first date from each of the first two time layout tables, assign to --> <!-- date1 and date2 --> <xsl:variable name="start-dates"> <xsl:apply-templates select="//data/time-layout[1]"/> </xsl:variable> <xsl:variable name="date1"> <xsl:value-of select="substring($start-dates, 1, 10)"/> </xsl:variable> <xsl:variable name="date2"> <xsl:value-of select="substring($start-dates, 26, 10)"/> </xsl:variable> <!-- if the first date in the first two layouts isn't the same day, then we will need to --> <!-- offset the table to get the days to line up. We'll use the tackOn variable as our flag --> <xsl:variable name="tackOn"> <xsl:choose> <xsl:when test="$date1=$date2">false</xsl:when> <xsl:otherwise>true</xsl:otherwise> </xsl:choose> </xsl:variable> <!-- main template --> <xsl:template match="/"> <!-- table of temperatures --> <table cellspacing="0"> <tr> <xsl:apply-templates select="//temperature"/> </tr> </table> <!-- credit --> <p class="white"> Courtesy: <xsl:element name="a"> <xsl:attribute name="class">whiteBold</xsl:attribute> <xsl:attribute name="href"><xsl:value-of select="//head/source/credit"/></xsl:attribute> <xsl:value-of select="//head/product/title"/> </xsl:element> </p> <!-- graphic forecasts --> <xsl:apply-templates select="//weather"/> </xsl:template> <!-- experimental templates --> <xsl:template match="//time-layout"> <!--<xsl:value-of select="child::layout-key"/>--> <xsl:apply-templates select="child::start-valid-time"/> </xsl:template> <!--<xsl:template match="//start-valid-time"> <xsl:if test="../layout-key='k-p24h-n7-1'"> <p> <xsl:value-of select="name()"/>: <xsl:value-of select="."/> </p> </xsl:if> </xsl:template>--> <!-- grab dates for comparison in initial variables --> <xsl:template match="//data/time-layout[1]"> <!-- grab first date --> <p> <xsl:value-of select="start-valid-time"/> </p> <!-- grab second date --> <p> <xsl:value-of select="following::start-valid-time"/> </p> </xsl:template> <!-- template to build table of temperature data --> <xsl:template match="temperature"> <xsl:variable name="times" select="key('layout-key', @time-layout)"/> <td valign="top"> <table width="200" cellspacing="0"> <xsl:for-each select="value"> <!-- if tackOn is true, add an offset to the row counter to make row --> <!-- colors align --> <xsl:variable name="offset"> <xsl:choose> <xsl:when test="$tackOn = 'true' and ../@type='maximum'">1</xsl:when> <xsl:otherwise>0</xsl:otherwise> </xsl:choose> </xsl:variable> <!-- set the color for this row using the mod function to see if even row --> <xsl:variable name="color"> <xsl:choose> <xsl:when test="(position() + $offset) mod 2 = 0">#CCCCCC</xsl:when> <xsl:otherwise>#FFFFFF</xsl:otherwise> </xsl:choose> </xsl:variable> <!-- if tackOn is true (at night) and max temperature, prepend row to top --> <xsl:choose> <xsl:when test="$tackOn='true' and position() = 1 and ../@type='maximum'"> <tr> <td colspan="2" bgcolor="#FFFFFF"> </td> </tr> </xsl:when> <xsl:otherwise/> </xsl:choose> <!-- start row --> <tr> <xsl:element name="td"> <xsl:attribute name="width">100</xsl:attribute> <xsl:attribute name="bgcolor"><xsl:value-of select="$color"/></xsl:attribute> <!-- while iterating set counter to current position in temperature array --> <xsl:variable name="counter" select="position()"/> <!-- dereference the position in the array to variable this-time --> <!-- (I tried to use position () here but it didn't work, probably a --> <!-- context issue) --> <xsl:variable name="this-time" select="$times/start-valid-time[$counter]"/> <!-- display value of the day name --> <xsl:value-of select="$this-time/@period-name"/> </xsl:element> <xsl:element name="td"> <xsl:attribute name="width">100</xsl:attribute> <xsl:attribute name="bgcolor"><xsl:value-of select="$color"/></xsl:attribute> <!-- the temperature --> <xsl:value-of select="."/> </xsl:element> </tr> <!-- if tackOn is true and minimum temperature, add row to bottom --> <xsl:choose> <xsl:when test="$tackOn='true' and position()=count(../value) and ../@type='minimum'"> <tr> <xsl:element name="td"> <xsl:attribute name="colspan">2</xsl:attribute> <xsl:attribute name="bgcolor">#CCCCCC</xsl:attribute> </xsl:element> </tr> </xsl:when> <xsl:otherwise/> </xsl:choose> </xsl:for-each> </table> </td> </xsl:template> <!-- template to make table of forecast graphics --> <xsl:template match="weather"> <xsl:variable name="times" select="key('layout-key', @time-layout)"/> <xsl:variable name="icons" select="//conditions-icon"/> <table width="400" cellspacing="1"> <xsl:for-each select="weather-conditions"> <tr> <!-- as above set the counter and deference times var to get --> <!-- corresponding day and time value --> <xsl:variable name="counter" select="position()"/> <xsl:variable name="this-time" select="$times/start-valid-time[$counter]"/> <td class="white"> <!-- pull the day name using the day key --> <xsl:variable name="day" select="key('day-key', substring($this-time, 1, 10))"/> <!-- <xsl:value-of select="substring($this-time, 1, 10)"/>--> <xsl:value-of select="$day/@period-name"/> <br/> <xsl:value-of select="substring($this-time, 12, 5)"/> </td> <td class="white"> <!-- if we have a text forecast, iterate through --> <!-- store in variable for image alt text --> <xsl:variable name="textForecast"> <xsl:choose> <xsl:when test="count(value) != 0"> <xsl:for-each select="value"> <xsl:value-of select="@coverage"/> <xsl:text> </xsl:text> <xsl:value-of select="@weather-type"/> <xsl:text> </xsl:text> </xsl:for-each> </xsl:when> <!-- if we don't have a text forecast --> <xsl:otherwise>No precipitation forecast</xsl:otherwise> </xsl:choose> </xsl:variable> <!-- repeat because we get nice text format --> <xsl:choose> <xsl:when test="count(value) != 0"> <xsl:for-each select="value"> <xsl:value-of select="@coverage"/> <xsl:text> </xsl:text> <xsl:value-of select="@weather-type"/> <br /> </xsl:for-each> </xsl:when> <!-- if we don't have a text forecast --> <xsl:otherwise>No precipitation forecast</xsl:otherwise> </xsl:choose> </td> <td> <!-- do da image thang --> <xsl:element name="img"> <xsl:attribute name="src"><xsl:value-of select="$icons/icon-link[$counter]"/>
</xsl:attribute> <xsl:attribute name="border">0</xsl:attribute> <xsl:attribute name="alt"><xsl:value-of select="$textForecast"/></xsl:attribute> </xsl:element> </td> </tr> </xsl:for-each> </table> </xsl:template> </xsl:stylesheet>
|
If you want to use any of this code, you're welcome to do so.
All I ask is that you credit me and this site (Brandt Barretto
at http://www.barretto.org/).
We appreciate your support!
|