The blog of roncli blog
Houston, Texas, United States
CTG Music
Trax in Space Beta
Recent Posts
Web Junkie
Lose That Buger Belly!
Sleep is Needed?
More on Textboxes
Making Textboxes Ain't Easy
Liquid War
#modarchive Story 2
Slow Going
Thursday, March 31, 2005
NWS XML web service for VB.Net
Posted: 5:35:00 PM 3 comments
Now playing: Atomship - Pencil Fight (3:38)

In my quest for consuming the NWS XML Web Service, I came across this blog post by Mikhail Arkhipov over at Microsoft. He has available for download a bit of code that allows you to consume said web service. Problem is, it's in C#.Net.

After reading the code and finally understanding NWS's NDFD format, I went ahead and converted the C#.Net code to VB.Net. Now, this isn't the first time I've done something with C# code, so it was fairly easy to go through the code and come up with a VB.Net equivalent. In fact, it was dead easy.

Anyway, I figured I'd post my conversion. The code is practically identical, except I chagned the variable names to suit the standards I'm used to.

First, create a new project and add a web reference to This will hook you into NWS's free service.

Second, add this bit of code to your project.

#Region "Structs"

    Private Structure DayWeatherData
        Public dtDate As Date
        Public strLowTempF As String
        Public strHighTempF As String
        Public strLowTempC As String
        Public strHighTempC As String
        Public strCloudIconURL As String
    End Structure

    Private Structure WeatherTable
        Public nodTimeLayout As XmlNode
        Public nodData As XmlNode
    End Structure

#End Region

#Region "NWS XML Reader"
    'Converted to VB.Net by Ronald M. Clifford
    'Original C#.Net by Mikhail Arkhipov

    Private Function FindLayoutTable(ByVal xmlDoc As XmlDocument, ByVal nodData As XmlNode)
        Dim nlTimeLayouts As XmlNodeList = xmlDoc.SelectNodes("/dwml/data/time-layout")
        Dim strTimeLayout As String = nodData.Attributes("time-layout").Value

        Dim node As XmlNode
        For Each node In nlTimeLayouts
            If strTimeLayout = node.SelectSingleNode("layout-key").InnerText Then
                Return node
            End If

        Return Nothing
    End Function

    Private Sub FillLowTemperatures(ByVal nodXML As XmlNode, ByRef dwdData() As DayWeatherData)
        Dim nlNodes As XmlNodeList = nodXML.SelectNodes("value")

        Dim intCount As Integer
        For intCount = 0 To UBound(dwdData) - 1
            dwdData(intCount).strLowTempF = nlNodes(intCount).InnerText
            dwdData(intCount).strLowTempC = Math.Round(5 * (Double.Parse(dwdData(intCount).strLowTempF) - 32) / 9).ToString
    End Sub

    Private Sub FillHighTemperatures(ByVal nodXML As XmlNode, ByRef dwdData() As DayWeatherData)
        Dim nlNodes As XmlNodeList = nodXML.SelectNodes("value")

        Dim intCount As Integer
        For intCount = 0 To UBound(dwdData) - 1
            dwdData(intCount).strHighTempF = nlNodes(intCount).InnerText
            dwdData(intCount).strHighTempC = Math.Round(5 * (Double.Parse(dwdData(intCount).strHighTempF) - 32) / 9).ToString
    End Sub

    Private Function ParseDateTime(ByVal str As String) As Date
        Return Date.Parse(str.Replace("T", " ").Substring(0, str.LastIndexOf("-")))
    End Function

    Private Sub FillDayNameAndTime(ByVal nodLows As XmlNodeList, ByVal nodHighs As XmlNodeList, ByRef dwdData() As DayWeatherData)
        ' Choose first elemnt from low or high list depending on what is close to the current time.
        ' Typically weather report is about 'today' or 'tonight' on the current day or about
        ' day temperatures for the coming days. Therefoce remaining elements always come from
        ' the high temperatures list.

        Dim dtFirstLow As Date = ParseDateTime(nodLows(0).InnerText)
        Dim dtFirstHigh As Date = ParseDateTime(nodHighs(0).InnerText)
        Dim dtNow As Date = Now
        Dim nlNodes As XmlNodeList

        If dtFirstLow.Day = dtNow.Day And dtFirstHigh.Day = dtNow.Day Then
            ' choose nearest
            Dim intDiffFromLow = Math.Abs(dtFirstLow.Hour - dtNow.Hour)
            Dim intDiffFromHigh = Math.Abs(dtFirstHigh.Hour - dtNow.Hour)

            If intDiffFromHigh < intDiffFromLow Then
                nlNodes = nodHighs
                nlNodes = nodLows
            End If
        ElseIf dtFirstHigh.Day = dtNow.Day Then
            ' choose highs
            nlNodes = nodHighs
            ' choose lows
            nlNodes = nodLows
        End If

        Dim intCount As Integer

        For intCount = 0 To UBound(dwdData) - 1
            Dim dt As Date = ParseDateTime(nlNodes(intCount).InnerText)

            If intCount = 0 Then
                dwdData(intCount).dtDate = dt
                dwdData(intCount).dtDate = New Date(dt.Year, dt.Month, dt.Day, 12, 0, 0)
            End If
    End Sub

    Private Sub FillCloudData(ByVal wt As WeatherTable, ByRef dwdData() As DayWeatherData)
        ' Cloud data is typically much longer than day high/low data
        ' We need to find times that match ones in high and low temp tables.
        Dim nlTimes As XmlNodeList = wt.nodTimeLayout.SelectNodes("start-valid-time")
        Dim nlIcons As XmlNodeList = wt.nodData.SelectNodes("icon-link")

        Dim intWeatherData As Integer = 0
        Dim intNodes As Integer = 0
        Dim intHourDiff As Integer = Integer.MaxValue

        Dim nodXML As XmlNode
        For Each nodXML In nlTimes
            Dim dt As Date = ParseDateTime(nodXML.InnerText)

            If dt.Date > dwdData(intWeatherData).dtDate.Date Then
                intWeatherData += 1

                If intWeatherData > UBound(dwdData) Then Exit For

                intHourDiff = Integer.MaxValue
            End If

            If dt.Date = dwdData(intWeatherData).dtDate.Date Then
                Dim intDiff As Integer = Math.Abs(dt.Hour - dwdData(intWeatherData).dtDate.Hour)

                If intDiff < intHourDiff Then
                    intHourDiff = intDiff
                    dwdData(intWeatherData).strCloudIconURL = nlIcons(intNodes).InnerText
                End If
            End If

            intNodes += 1
    End Sub

    Private Function ParseWeatherXML(ByVal strXMLWeather) As DayWeatherData()
            Dim dwd() As DayWeatherData
            Dim xmlDoc As New XmlDocument()

            ' load XML data into a tree

            ' locate dwml/data/time-layout nodes. There should be three of them: 
            '      - next week nighttime temperatures (lows)
            '      - next week daytime temperatures (highs)
            '      - next week cloud data

            ' Find roots nodes for temperature and cloud data
            Dim wtLowTemp As New WeatherTable()
            Dim wtHighTemp As New WeatherTable()
            Dim wtClouds As New WeatherTable()

            wtLowTemp.nodData = xmlDoc.SelectSingleNode("/dwml/data/parameters/temperature[@type='minimum']")
            wtHighTemp.nodData = xmlDoc.SelectSingleNode("/dwml/data/parameters/temperature[@type='maximum']")
            wtClouds.nodData = xmlDoc.SelectSingleNode("/dwml/data/parameters/conditions-icon")

            ' Find out corresponding time layout table for each top data node
            wtLowTemp.nodTimeLayout = FindLayoutTable(xmlDoc, wtLowTemp.nodData)
            wtHighTemp.nodTimeLayout = FindLayoutTable(xmlDoc, wtHighTemp.nodData)
            wtClouds.nodTimeLayout = FindLayoutTable(xmlDoc, wtClouds.nodData)

            ' Number of day data is min of low and high temperatures
            Dim nlLowTimes As XmlNodeList = wtLowTemp.nodTimeLayout.SelectNodes("start-valid-time")
            Dim nlHighTimes As XmlNodeList = wtHighTemp.nodTimeLayout.SelectNodes("start-valid-time")

            Dim intTimes As Integer = Math.Min(nlLowTimes.Count, nlHighTimes.Count)

            Dim dwdData(intTimes) As DayWeatherData

            Dim intCount As Integer
            For intCount = 0 To intTimes - 1
                dwdData(intCount) = New DayWeatherData()

            ' Fill highs and lows
            FillLowTemperatures(wtLowTemp.nodData, dwdData)
            FillHighTemperatures(wtHighTemp.nodData, dwdData)

            FillDayNameAndTime(nlLowTimes, nlHighTimes, dwdData)

            FillCloudData(wtClouds, dwdData)

            Return dwdData

        Catch ex As Exception
            Return Nothing
        End Try
    End Function

#End Region

Finally, simply make a call to their web service.

        Dim weather As New
        Dim params As New
        params.maxt = True = True
        params.icons = True
        Dim strWeatherXML As String = weather.NDFDgen(39, -77, "time-series", Now, Now.AddDays(5), params)
        weather = Nothing
        params = Nothing
        Dim dwdWeather() As DayWeatherData = ParseWeatherXML(strWeatherXML)

You now have a DayWeatherData object, dwdWeather, that contains the highs and lows for the day, along with a URL to an icon that displays the type of weather to expect for the day. This is ultra basic. If you check out the NWS site, you'll find there are many other options you can add in, including cloud cover, percipitation chance and amount, and a couple other things. You'll have to code the XML reader to parse these things, but it's pretty easily done. And best of all, it's free!

The only down side to this is that you need to come up with the latitude and longitude (negative for the western hemisphere) for the location you want to use, which means you'll need to grab a Zip Code to latitude & longitude database.

Labels: , ,


  Blogger cloud9ine @ 12/18/2008 03:20:00 PM:
Thanks dude, but i need help

I am so so there. I have dwdweather in hand. I am new to VB so I have no idea how to reach each item within.

I've tried a . but it tells me it is a system.array

Lost here. Help please?

  Blogger roncli @ 12/18/2008 06:23:00 PM:
dwdWeather is an array, so you need to treat it just like you'd treat any other array. You could call each item individually:


Or use a For Each loop in order to access each of the items in order.

For Each dwd As DayWeatherData in dwdWeather
' Do something with the dwd object here.

  Blogger S.Sukhotinsky @ 1/05/2009 11:52:00 AM:

Post a Comment