roncli.com blog
The blog of roncli
roncli.com blog
roncli.com
blog
Profile
roncli
Houston, Texas, United States
Labels
Coding
CTG Music
Editorials
Games
Miscellaneous
Music
Servers
Silliness
Software
Sports
Trax in Space Beta
Weather
Recent Posts
WTF did Revival Productions just do?
A Tale of Two Communities
The Final Stretch
A Two Tiered, Untiered OTL
Secretly, you wish you could've done what I did
What have I done since roncli.com v2?
It's Done. It's Finally Done.
The Big Picture is Starting to Wear on Me
A Low Bang to Buck Ratio
win-acme
Archives
February 2005
March 2005
April 2005
May 2005
June 2005
July 2005
August 2005
September 2005
October 2005
November 2005
December 2005
January 2006
February 2006
March 2006
April 2006
May 2006
June 2006
July 2006
August 2006
September 2006
October 2006
November 2006
December 2006
February 2007
March 2007
April 2007
May 2007
June 2007
July 2007
August 2007
September 2007
October 2007
November 2007
December 2007
January 2008
February 2008
March 2008
April 2008
June 2008
July 2008
September 2008
December 2008
February 2009
July 2009
August 2009
September 2009
October 2009
November 2009
February 2010
March 2010
April 2010
June 2010
July 2010
August 2010
September 2010
October 2010
November 2010
December 2010
March 2011
June 2011
July 2011
August 2011
September 2011
October 2011
December 2011
January 2012
February 2012
April 2012
July 2012
November 2012
July 2013
April 2014
July 2014
August 2014
November 2014
December 2014
March 2015
April 2015
May 2015
June 2015
July 2015
September 2015
January 2016
February 2016
May 2016
July 2016
November 2016
March 2017
January 2018
May 2018
June 2018
January 2019
January 2021
February 2021
March 2021
August 2021
October 2021
December 2021
August 2022
November 2022
October 2023
February 2024
April 2024
Thursday, July 14, 2005
First National Radar Test
Posted: 11:01:00 PM 0 comments
Now playing: Namco - Sala de Masala 2000 (2:42)

I completed my first national radar test based on NWS's data. Not quite where I would like it to be, as there is ground clutter and in the northwest there are two strange looking lines that are obviously not supposed to be there... apparently NWS doesn't filter them out automatically. You can see a comparable image at approximately the same time to see the differences.

The good news is that the code obviously works. The bad news is that it took over 11 minutes to process all 136 radar sites in the continental US. NWS would have made two updates on each of those 136 radar sites in that time, not to mention that my radar data is now 11 minutes older than it was.

A good percentage of that time is spent downloading the radar data from NWS to begin with. Considering I'm on a shared T3 and it took about 3 to 4 minutes to download less than 2 MB of data, that's relatively slow.

But processing the files is taking the bulk of the time, and I may have to revisit the methods I use in order to get this to work faster. It could just be that I'm in debug mode, but I doubt that's going to help with the download speeds. Also, a lot of this is just computationally expensive. You figure there are 230 points I'm looking at for each degree around the radar - all 360 of them - for each of 136 radars. That's over 11 million pixels of data. Plus, since the map I'm displaying it on is a latitude/longitude map, I have to take into account that a nautical mile at 30 degrees of latitude is not the same as a nautical mile at 40 degrees of latitude. It gets complex, but I got a pretty good algorithm setup to do it.

So in short, there's a long way to go yet before this is practical, but the ground work and understanding is there, which is usually 80% of the work. I'll probably post more on this in the future.

Labels: , ,

Tuesday, July 12, 2005
More NWS stuff
Posted: 7:18:00 PM 0 comments
Now playing: roncli, The Nightstalker - Everytime (5:24)

While this is not related at all to the project I was working on the other day, I thought I'd present this interesting test I've been doing.

Today I worked on parsing WSR-88D data. In short, it's data available for free on the NWS site that, when decoded, allows you to display radar images. I gotta say, this is pretty simple work when you know what you're doing. Check out this current image of Houston area radar.



Although there are no borders, the ground clutter makes it easy to see Galveston and the bay. The northern end of the image reaches Dallas, while the western end extends beyond San Antonio and Austin.

It should only be a matter of putting the pieces together in order to get not only radar that's more pretty, but something that'll pave the way into locally cached radar loops for insane time ranges that you just can't get on other sites, free or pay.

The ultimate goal, of course, would be to import these images into Google Earth. They do have one radar service I saw, but it wasn't very good because it had the company's logo written all over the freakin' map.

In any case, between this and the other NWS stuff I've worked on earlier this year, I almost have enough to put together for a desktop weather app. Might be interesting to see how far I can go with this.

Labels: , ,

Wednesday, July 06, 2005
Stagnant and frustrated
Posted: 1:42:00 PM 0 comments
Now playing: Antony Raijekov - Mapped (5:39)

Not a whole lot has been going on. I've become pretty stagnant over the last month. That sucks.

Not that I haven't been doing anything, though. Those that have the slightest of a conjecture as to what I've been doing should not be surprised in knowing that I've been kept busy with some new stuff over the past month. The "newness" of it has worn off, so hopefully that means I'll be working on my own stuff again.

Last couple of days has seen me working on a project using .NET with TCP/IP sockets and threading. It's scary the ease with which I understand this stuff, I had a working prototype in a matter of like an hour and a half. But the interesting thing to me is how this project came about in the first place... simply put, frustration with using something similar, and the drive to do it better. Lately, though, more than that has been needed for me to get anything done. Plus, even if I complete this project, I know for a fact that it won't get used. I guess it's more for personal "can I do it" curiosity.

The project I had posted about with the NWS stuff before has stalled entirely. Turns out the guy I was going to be working on it with moved, and it was decided the effort to do it wasn't work it. Of course, he has *since* told me that it might be up and running again... I don't know. Sometimes I feel like I should just work on the project and then one day dump the finished product into his lap and say "do something with this, dammit". Maybe that's what I need to do.

And on a final note of frustration, I decided to process my logs for the first time after three months. What a painfully long and ridiculous process that has turned out to be, I'm going to have to write something that does this for me daily.

Labels: , , ,

Friday, April 01, 2005
NWS? XML? NDFD? XPATH?
Posted: 2:38:00 PM 0 comments
Now playing: Atomship - Pencil Fight (3:38)

Seems there's an acronym for everything these days. I guess I'm one of the few that prefer to write out my words. You know, self-documentation and all.

Anyway, I am apparantly on a roll with the NWS stuff I've been working on. I can now read the forecast, current conditions, and severe weather alerts for a particular zip code, reloading as appropriate. All the hard work is done, which means the fun stuff is next, displaying the data. I have a pretty good idea of what I want to do for this, and it'll be quite fun to work on. Having learned a bit about VB.Net's 2D drawing capability and already having played around with Crystal Space's 2D drawing, I have a pretty good idea what I'm in for, so it shouldn't be too difficult to figure out.

Labels: , , ,

Thursday, March 31, 2005
Updated NWS Code
Posted: 10:17:00 PM 0 comments
Now playing: Atomship - Pencil Fight (3:38)

Mikhail's code was enough to get me started on combining all of the various parameters I wanted (just the relative ones, none of this dewpoint crap) together to come up with one single variable that has all of the forecast possible for one location.

#Region "Structs"

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

    Private Structure WeatherData
        Public wtHighTemp As WeatherTable
        Public wtLowTemp As WeatherTable
        Public wtPointTemp As WeatherTable
        Public wtLiquidPrecip As WeatherTable
        Public wtSnowPrecip As WeatherTable
        Public wtPrecipProb As WeatherTable
        Public wtWindSpeed As WeatherTable
        Public wtWindDirection As WeatherTable
        Public wtCloudCover As WeatherTable
    End Structure

    Private Structure TemperatureData
        Public F As Integer
        Public ReadOnly Property C() As Integer
            Get
                Return Integer.Parse(Math.Round(((Double.Parse(F) - 32) / 9) * 5))
            End Get
        End Property
        Public dt As Date
    End Structure

    Private Structure PrecipitationData
        Public Inches As Double
        Public ReadOnly Property MM() As Double
            Get
                Return Inches * 25.4
            End Get
        End Property
        Public dt As Date
    End Structure

    Private Structure PercentData
        Public Pct As Integer
        Public dt As Date
    End Structure

    Private Structure WindData
        Public Knots As Integer
        Public ReadOnly Property MPH() As Integer
            Get
                Return Integer.Parse(Math.Round((Double.Parse(Knots) * 6076.12) / 5280))
            End Get
        End Property
        Public Degrees As Integer
        Public ReadOnly Property Dir() As String
            Get
                Select Case Degrees
                    Case 0 To 11.25, 348.75 To 360
                        Return "N"
                    Case 11.25 To 33.75
                        Return "NNE"
                    Case 33.75 To 56.25
                        Return "NE"
                    Case 56.25 To 78.75
                        Return "ENE"
                    Case 78.75 To 101.25
                        Return "E"
                    Case 101.25 To 123.75
                        Return "ESE"
                    Case 123.75 To 146.25
                        Return "SE"
                    Case 146.25 To 168.75
                        Return "SSE"
                    Case 168.75 To 191.25
                        Return "S"
                    Case 191.25 To 213.75
                        Return "SSW"
                    Case 213.75 To 236.25
                        Return "SW"
                    Case 236.25 To 258.75
                        Return "WSW"
                    Case 258.75 To 281.25
                        Return "W"
                    Case 281.25 To 303.75
                        Return "WNW"
                    Case 303.75 To 326.25
                        Return "NW"
                    Case 326.25 To 348.75
                        Return "NNW"
                End Select
            End Get
        End Property
        Public dt As Date
    End Structure

    Private Structure FormattedWeatherData
        Public HighTemp() As TemperatureData
        Public LowTemp() As TemperatureData
        Public PointTemp() As TemperatureData
        Public LiquidPrecip() As PrecipitationData
        Public SnowPrecip() As PrecipitationData
        Public PrecipProb() As PercentData
        Public Wind() As WindData
        Public CloudCover() As PercentData
    End Structure

#End Region

#Region "NWS XML Reader http://www.nws.noaa.gov/forecasts/xml"

    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
        Next

        Return Nothing
    End Function

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

    Private Sub FillTemperatureData(ByVal wt As WeatherTable, ByRef temp() As TemperatureData)
        Dim intCount As Integer

        Dim nlData As XmlNodeList = wt.nodData.SelectNodes("value")
        Dim nlDate As XmlNodeList = wt.nodTimeLayout.SelectNodes("start-valid-time")
        For intCount = 0 To UBound(temp) - 1
            temp(intCount) = New TemperatureData()
            temp(intCount).F = Integer.Parse(nlData(intCount).InnerText)
            temp(intCount).dt = ParseDateTime(nlDate(intCount).InnerText)
        Next
    End Sub

    Private Sub FillPrecipitationData(ByVal wt As WeatherTable, ByRef precip() As PrecipitationData)
        Dim intCount As Integer

        Dim nlData As XmlNodeList = wt.nodData.SelectNodes("value")
        Dim nlDate As XmlNodeList = wt.nodTimeLayout.SelectNodes("start-valid-time")
        For intCount = 0 To UBound(precip) - 1
            precip(intCount) = New PrecipitationData()
            If Len(nlData(intCount).InnerText) = 0 Then
                precip(intCount).Inches = 0
            Else
                precip(intCount).Inches = Double.Parse(nlData(intCount).InnerText)
            End If
            precip(intCount).dt = ParseDateTime(nlDate(intCount).InnerText)
        Next
    End Sub

    Private Sub FillPercentData(ByVal wt As WeatherTable, ByRef pct() As PercentData)
        Dim intCount As Integer

        Dim nlData As XmlNodeList = wt.nodData.SelectNodes("value")
        Dim nlDate As XmlNodeList = wt.nodTimeLayout.SelectNodes("start-valid-time")
        For intCount = 0 To UBound(pct) - 1
            pct(intCount) = New PercentData()
            pct(intCount).Pct = Integer.Parse(nlData(intCount).InnerText)
            pct(intCount).dt = ParseDateTime(nlDate(intCount).InnerText)
        Next
    End Sub

    Private Sub FillWindData(ByVal wtSpeed As WeatherTable, ByVal wtDir As WeatherTable, ByRef wind() As WindData)
        Dim intCount As Integer

        Dim nlSpeed As XmlNodeList = wtSpeed.nodData.SelectNodes("value")
        Dim nlDir As XmlNodeList = wtDir.nodData.SelectNodes("value")
        Dim nlDate As XmlNodeList = wtSpeed.nodTimeLayout.SelectNodes("start-valid-time")
        For intCount = 0 To UBound(wind) - 1
            wind(intCount) = New WindData()
            wind(intCount).Knots = Integer.Parse(nlSpeed(intCount).InnerText)
            wind(intCount).Degrees = Integer.Parse(nlDir(intCount).InnerText)
            wind(intCount).dt = ParseDateTime(nlDate(intCount).InnerText)
        Next
    End Sub

    Private Function ParseForecastXML(ByVal strXMLWeather) As FormattedWeatherData
        Try
            'Setup variables
            Dim xmlDoc As New XmlDocument()
            Dim wdData As New WeatherData()
            Dim fwdData As New FormattedWeatherData()
            wdData.wtHighTemp = New WeatherTable()
            wdData.wtLowTemp = New WeatherTable()
            wdData.wtPointTemp = New WeatherTable()
            wdData.wtLiquidPrecip = New WeatherTable()
            wdData.wtSnowPrecip = New WeatherTable()
            wdData.wtPrecipProb = New WeatherTable()
            wdData.wtWindSpeed = New WeatherTable()
            wdData.wtWindDirection = New WeatherTable()
            wdData.wtCloudCover = New WeatherTable()

            'Load XML data
            xmlDoc.LoadXml(strXMLWeather)

            'Load data and their corresponding time nodes
            wdData.wtHighTemp.nodData = xmlDoc.SelectSingleNode("/dwml/data/parameters/temperature[@type='maximum']")
            wdData.wtLowTemp.nodData = xmlDoc.SelectSingleNode("/dwml/data/parameters/temperature[@type='minimum']")
            wdData.wtPointTemp.nodData = xmlDoc.SelectSingleNode("/dwml/data/parameters/temperature[@type='hourly']")
            wdData.wtLiquidPrecip.nodData = xmlDoc.SelectSingleNode("/dwml/data/parameters/precipitation[@type='liquid']")
            wdData.wtSnowPrecip.nodData = xmlDoc.SelectSingleNode("/dwml/data/parameters/precipitation[@type='snow']")
            wdData.wtPrecipProb.nodData = xmlDoc.SelectSingleNode("/dwml/data/parameters/probability-of-precipitation[@type='12 hour']")
            wdData.wtWindSpeed.nodData = xmlDoc.SelectSingleNode("/dwml/data/parameters/wind-speed[@type='sustained']")
            wdData.wtWindDirection.nodData = xmlDoc.SelectSingleNode("/dwml/data/parameters/direction[@type='wind']")
            wdData.wtCloudCover.nodData = xmlDoc.SelectSingleNode("/dwml/data/parameters/cloud-amount[@type='total']")

            wdData.wtHighTemp.nodTimeLayout = FindLayoutTable(xmlDoc, wdData.wtHighTemp.nodData)
            wdData.wtLowTemp.nodTimeLayout = FindLayoutTable(xmlDoc, wdData.wtLowTemp.nodData)
            wdData.wtPointTemp.nodTimeLayout = FindLayoutTable(xmlDoc, wdData.wtPointTemp.nodData)
            wdData.wtLiquidPrecip.nodTimeLayout = FindLayoutTable(xmlDoc, wdData.wtLiquidPrecip.nodData)
            wdData.wtSnowPrecip.nodTimeLayout = FindLayoutTable(xmlDoc, wdData.wtSnowPrecip.nodData)
            wdData.wtPrecipProb.nodTimeLayout = FindLayoutTable(xmlDoc, wdData.wtPrecipProb.nodData)
            wdData.wtWindSpeed.nodTimeLayout = FindLayoutTable(xmlDoc, wdData.wtWindSpeed.nodData)
            wdData.wtWindDirection.nodTimeLayout = FindLayoutTable(xmlDoc, wdData.wtWindDirection.nodData)
            wdData.wtCloudCover.nodTimeLayout = FindLayoutTable(xmlDoc, wdData.wtCloudCover.nodData)

            'Setup formatted data variables
            ReDim fwdData.HighTemp(wdData.wtHighTemp.nodTimeLayout.SelectNodes("start-valid-time").Count)
            ReDim fwdData.LowTemp(wdData.wtLowTemp.nodTimeLayout.SelectNodes("start-valid-time").Count)
            ReDim fwdData.PointTemp(wdData.wtPointTemp.nodTimeLayout.SelectNodes("start-valid-time").Count)
            ReDim fwdData.LiquidPrecip(wdData.wtLiquidPrecip.nodTimeLayout.SelectNodes("start-valid-time").Count)
            ReDim fwdData.SnowPrecip(wdData.wtSnowPrecip.nodTimeLayout.SelectNodes("start-valid-time").Count)
            ReDim fwdData.PrecipProb(wdData.wtPrecipProb.nodTimeLayout.SelectNodes("start-valid-time").Count)
            ReDim fwdData.Wind(wdData.wtWindSpeed.nodTimeLayout.SelectNodes("start-valid-time").Count)
            ReDim fwdData.CloudCover(wdData.wtCloudCover.nodTimeLayout.SelectNodes("start-valid-time").Count)

            'Fill in data
            FillTemperatureData(wdData.wtHighTemp, fwdData.HighTemp)
            FillTemperatureData(wdData.wtLowTemp, fwdData.LowTemp)
            FillTemperatureData(wdData.wtPointTemp, fwdData.PointTemp)
            FillPrecipitationData(wdData.wtLiquidPrecip, fwdData.LiquidPrecip)
            FillPrecipitationData(wdData.wtSnowPrecip, fwdData.SnowPrecip)
            FillPercentData(wdData.wtPrecipProb, fwdData.PrecipProb)
            FillWindData(wdData.wtWindSpeed, wdData.wtWindDirection, fwdData.Wind)
            FillPercentData(wdData.wtCloudCover, fwdData.CloudCover)

            Return fwdData

        Catch ex As Exception
            Return Nothing
        End Try
    End Function

#End Region


A little more complicated in some ways, a bit simpler in others, but the data is complete. To call the web service and retrieve the data:

        Dim weather As New gov.weather.ndfdXML()
        Dim params As New gov.weather.weatherParametersType()
        params.maxt = True
        params.mint = True
        params.temp = True
        params.qpf = True
        params.snow = True
        params.pop12 = True
        params.wspd = True
        params.wdir = True
        params.sky = True
        Dim fwdWeather as FormattedWeatherData = ParseForecastXML(weather.NDFDgen(sngLatitude, sngLongitude, "time-series", Now, Now.AddDays(30), params))
        weather = Nothing
        params = Nothing


fwdWeather now has all the forecasting data you need. It also has easy functions to convert F to C, Knots to MPH, and Inches to Millimeters.

Not bad for a day of research and coding! Now if only they'd make web services for current conditions and severe weather statements, I'd be all set.

Labels: , ,

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 http://weather.gov/forecasts/xml/SOAP_server/ndfdXMLserver.php?wsdl. 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 http://www.nws.noaa.gov/forecasts/xml"
    '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
        Next

        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
        Next
    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
        Next
    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
            Else
                nlNodes = nodLows
            End If
        ElseIf dtFirstHigh.Day = dtNow.Day Then
            ' choose highs
            nlNodes = nodHighs
        Else
            ' 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
            Else
                dwdData(intCount).dtDate = New Date(dt.Year, dt.Month, dt.Day, 12, 0, 0)
            End If
        Next
    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
        Next
    End Sub

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

            ' load XML data into a tree
            xmlDoc.LoadXml(strXMLWeather)

            ' 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()
            Next

            ' 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 gov.weather.ndfdXML()
        Dim params As New gov.weather.weatherParametersType()
        params.maxt = True
        params.mint = 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: , ,

Web Junkie
Posted: 5:16:00 PM 0 comments
Now playing: Atomship - Pencil Fight (3:38)

Here it is about 5:15 in the afternoon. I've been up since about 8 in the morning, mostly trying to figure out the NWS XML web service. I eventually got it (more on that in my next post) thanks to a website that had downloadable code available that consumes the service.

So, I wanted to make a post here in my blog about it. I open up Crazy Browser, got to the history, and click on "today". How many freaking websites did I visit today?! The list is absolutely ENORMOUS, showing over 100 sites.

Am I a junkie or what?

Labels: , , ,