|
|
Thursday, March 31, 2005 |
NWS XML web service for VB.Net
Posted: 5:35:00 PM
|
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: Coding, NWS, VB.NET
3 Comments
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?
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:
dwdWeather(0)
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. Next
S.Sukhotinsky @ 1/05/2009 11:52:00 AM:
Thanks
|
|