Got weather?

Hot on the heels of my update to the Latitude/Longitude functions page, I've written some more VBA code that uses web APIs to retrieve weather information for any zip code in the U.S.A. If you visit that page and then look at the code below, you may notice the similarity.

It's pretty flexible, if I do say so myself. You can look at weather data for up to the next five days (including the current date) for any U.S. zip code, and return the high or low temp in Celsius or Fahrenheit! I smell a weather app on the horizon.

First place the following at the top of a standard module:

Const XML_FILE_EXTENSION As String = ".xml"

Public Enum MaxOrMinTemp
  HighC
  LowC
  HighF
  LowF
End Enum

The constant isn't really required, feel free to remove it and hard code the .xml file extension. The enum section delimits what types of temperatures we can select. Respectively, they represent

  • Celsius daily high temp
  • Celsius daily low temp
  • Fahrenheit daily high temp
  • Fahrenheit daily low temp

The Main Function

Now on to the good stuff. Note that this is just one of many possible applications of the weather API. See below for code walkthrough.

Function ZipCodeWeather(ZipCode As String, daytoCheck As Date, _
    Optional temperature As MaxOrMinTemp = HighF) As Long
' http://www.webservicex.net/WCF/ServiceDetails.aspx?SID=44
' returns given weather for given U.S. zip code on given day

Dim xml As Object
Dim result As String
Dim nextFileNum As Long
Dim tempFile As String
Dim doc As Object
Dim objRoot As Object
Dim Children As Object
Dim Child As Object
Dim subChildren As Object
Dim subChild As Object
Dim subsubChildren As Object
Dim subsubChild As Object
Dim dayWasFound As Boolean
Dim whichTemp As String

  ' check if valid day selected
  ' (must be 5 days or less from today, per API)
  If ((daytoCheck - Now > 5) Or (daytoCheck - Now < 1)) Then
    ' MsgBox "Date to check must be 5 or less days from today."
    ' return -999 instead of zero, because weather might actually be zero!
    ZipCodeWeather = -999
    Exit Function
  End If

  ' check valid zip
  If Len(ZipCode) <> 5 Then
    ZipCodeWeather = -999
    Exit Function
  End If

  Set xml = CreateObject("MSXML2.XMLHTTP.6.0")

  xml.Open "GET", _
"http://www.webservicex.net/WeatherForecast.asmx/GetWeatherByZipCode?ZipCode=" & ZipCode, False
  xml.Send

  result = xml.responsetext

 ' put "?result" into Immediate Window
 ' if you want to see the return result

  ' write to temp xml file
  nextFileNum = FreeFile

  tempFile = Environ("temp") & Application.PathSeparator & ZipCode & XML_FILE_EXTENSION

  Open tempFile For Output As #nextFileNum
  Print #nextFileNum, result
  Close #nextFileNum

  Set doc = CreateObject("MSXML2.DOMDocument")
  doc.validateOnParse = False
  doc.Load tempFile

  Set objRoot = doc.documentElement

  Set Children = objRoot.childnodes

  ' do we want high or low temp, celsius or fahrenheit?
  Select Case temperature
    Case 0 ' high celsius
      whichTemp = "MaxTemperatureC"
    Case 1 ' low celsius
      whichTemp = "MinTemperatureC"
    Case 2 ' high fahrenheit
      whichTemp = "MaxTemperatureF"
    Case 3 ' low fahrenheit
      whichTemp = "MinTemperatureF"
  End Select

  For Each Child In Children
    If Child.nodename = "Details" Then
      Set subChildren = Child.childnodes
      For Each subChild In subChildren
        Set subsubChildren = subChild.childnodes
        For Each subsubChild In subsubChildren
          If subsubChild.Text = Format(daytoCheck, "dddd, mmmm dd, yyyy") Then
            ' current iteration is the correct date
            dayWasFound = True
          End If

          If dayWasFound Then

            If subsubChild.nodename = whichTemp Then
              ZipCodeWeather = subsubChild.Text
              Kill tempFile
              Exit Function
            End If
          End If
        Next subsubChild
      Next subChild
    End If
  Next Child

End Function

Since the XML response from the web will only contain five days' worth of weather information, the first thing we do is check if the date is valid. We also check if the zip code is valid; unfortunately other than checking for five digits (or possibly that it's a number), there's little validation possible (I'd be glad to hear otherwise).

If either of these conditions fails, the function returns a value of -999. This is necessary because by default, it will return zero. But the temperature could be zero, so you wouldn't know the function had failed. The -999 result should be checked by a wrapper procedure to make sure the result is legit.

A temporary XML file is created, then loaded into a MSXML Document Object. The temperature is checked (Fahrenheit high temp is assumed) and the appropriate string is applied. The XML is parsed until the correct day is found. Then the appropriate temperature from that day is parsed and returned by the function, but not before the temporary XML file is deleted.

Sample usage

The following sample procedure defines a zip code (which happens to be mine) and then displays the high temperature prediction in Celsius for Wednesday.

Sub TestZipCodeWeather()

Dim ZipCode As String
Dim filePath As String

  ZipCode = "11103"

  MsgBox ZipCodeWeather(ZipCode, #3/17/2010#, HighC)

End Sub

Download sample workbook — Excel 2003
Download sample workbook — Excel 2007

About JP

I'm just an average guy who writes VBA code for a living. This is my personal blog. Excel and Outlook are my thing, with a sprinkle of Access and Word here and there. Follow this space to learn more about VBA. Keep Reading »



Share This Article:

Share and bookmark this articledelicious buttonfacebook buttonlinkedin buttonstumbleupon buttontwitter button

This article is closed to new comments. Why?
learn excel dashboards